装饰器模式

装饰器模式

装饰模式就是将其实现的功能拆分成一个个装饰器类,然后这些装饰器类实现功能并组装起来达成预期功能,还可以在不知不觉中增加其他装饰器增加新功能,而被装饰类是无感知的

1.装饰器模式的本质

装饰模式的本质:动态组合。

装饰器模式是一种结构型设计模式,它允许在不改变现有对象结构的情况下,动态地将新行为添加到对象中。它通过将对象包装在具有相同接口的装饰器类中,然后通过组合来添加新的功能或修改原始对象的行为。

装饰器模式的本质在于通过组合而非继承的方式来扩展对象的功能。它提供了一种灵活的方式来添加或修改对象的行为,同时遵循开放封闭原则(Open-Closed Principle)。通过使用装饰器模式,可以在运行时动态地添加、移除或修改对象的功能,而无需修改现有的代码。

2.何时选用装饰器模式

建议在以下情况中选用装饰模式。

  • 在不改变现有对象接口的情况下,需要动态地添加额外功能或修改对象的行为。

  • 需要通过组合而非继承来扩展对象的功能,以避免类爆炸或静态绑定的限制。

  • 需要在运行时动态地添加、移除或修改对象的功能,而不影响其他对象。

  • 需要对现有的对象进行功能扩展,但是不希望对现有代码进行大规模修改。

3.优缺点

装饰模式有以下优点。

灵活性

  • 装饰器模式允许在运行时动态地添加、移除或修改对象的功能,而无需修改现有的代码。它提供了一种灵活的方式来扩展对象的行为。

开放封闭原则

  • 装饰器模式遵循开放封闭原则,即对扩展是开放的,对修改是封闭的。通过装饰器模式,可以在不修改现有代码的情况下,通过添加装饰器来扩展对象的功能。

可组合性

  • 由于装饰器模式通过组合的方式来添加功能,可以使用多个装饰器进行组合,实现不同的行为组合。这使得装饰器具有很高的可组合性,可以按需组合装饰器,以满足不同的需求。

缺点:
复杂性增加

  • 使用装饰器模式会引入许多额外的类和对象,这增加了代码的复杂性和理解难度。

装饰器顺序依赖

  • 装饰器的顺序是有意义的,不同的装饰器组合可能会产生不同的结果。因此,需要仔细管理和组织装饰器的顺序。

4.装饰器模式的结构

在这里插入图片描述

  • Component:组件对象的接口,可以给这些对象动态地添加职责。
  • ConcreteComponent:具体的组件对象,实现组件对象接口,通常就是被装饰器装饰的原始对象,.也就是可以给这个对象添加职责。
  • Decorator:所有装饰器的抽象父类,需要定义一个与组件接口一致的接口,并持有一个Component对象,其实就是持有一个被装饰的对象。

5.实现

不使用设计模式写法

1.咖啡类

/**
 * 咖啡接口
 */
interface Coffee {
    /**
     * 获取描述信息
     * @return
     */
    String getDescription();

    /**
     * 获取花费
     * @return
     */
    double getCost();
}

/**
 * 黑咖啡
 */
class BlackCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "黑咖啡";
    }

    @Override
    public double getCost() {
        return 2.0;
    }
}

2.测试类

public class Client {

    public static void main(String[] args) {
        // 订一杯黑咖啡
        Coffee coffee = new BlackCoffee();
        System.out.println("订单:" + coffee.getDescription());
        System.out.println("总价:" + coffee.getCost());
    }
}

3.结果

订单:黑咖啡
总价:2.0

功能实现完,又想给咖啡加糖,以为直接在BlackCoffee 类中加个方法就行了,但实际上不能这么做,这么做违反了开闭原则。

那怎么实现新增功能呢,新增一个子类,让他继承BlackCoffee 即可,但如果还要加功能就还要新增子类,子类或越来越庞大。

4.给咖啡加糖

/**
 * 加糖黑咖啡
 */
class SugarBlackCoffee extends BlackCoffee {
    @Override
    public String getDescription() {
        return super.getDescription() + ", 加糖";
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.3;
    }
}

5.修改测试类

public class Client {

    public static void main(String[] args) {
    	// 加糖黑咖啡
        Coffee sugarBlackCoffee = new SugarBlackCoffee();
        System.out.println("订单:" + sugarBlackCoffee.getDescription());
        System.out.println("总价:" + sugarBlackCoffee.getCost());
    }
}

6.结果

订单:黑咖啡, 加糖
总价:2.3

如果想要在不继承类的情况下增加功能,一种较好的方式是增加装饰器模式

使用设计模式优化

在这里插入图片描述

1.定义组件接口及真实实现对象

/**
 * 咖啡接口
 */
interface Coffee {
    /**
     * 获取描述信息
     * @return
     */
    String getDescription();

    /**
     * 获取花费
     * @return
     */
    double getCost();
}

/**
 * 黑咖啡
 */
class BlackCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "黑咖啡";
    }

    @Override
    public double getCost() {
        return 2.0;
    }
}

2.装饰器抽象类及具体的装饰器对象

/**
 * 咖啡装饰器抽象类
 */
abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription();
    }

    @Override
    public double getCost() {
        return coffee.getCost();
    }
}

/**
 * 真实的糖装饰器
 */
class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", 加糖";
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.3;
    }
}

/**
 * 真实的奶装饰器
 */
class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", 加牛奶";
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.5;
    }
}

3.测试类

public class Main {
    public static void main(String[] args) {
        // 订一杯黑咖啡
        Coffee coffee = new BlackCoffee();
        System.out.println("订单:" + coffee.getDescription());
        System.out.println("总价:" + coffee.getCost());

        // 加牛奶
        coffee = new MilkDecorator(coffee);
        System.out.println("订单:" + coffee.getDescription());
        System.out.println("总价:" + coffee.getCost());

        // 再加糖
        coffee = new SugarDecorator(coffee);
        System.out.println("订单:" + coffee.getDescription());
        System.out.println("总价:" + coffee.getCost());

        // 加两份糖,一份奶的黑咖啡
        MilkDecorator coffee2 = new MilkDecorator(new SugarDecorator(new SugarDecorator(new BlackCoffee())));
        System.out.println("订单:" + coffee2.getDescription());
        System.out.println("总价:" + coffee2.getCost());
    }
}

4.结果

订单:黑咖啡
总价:2.0
订单:黑咖啡, 加牛奶
总价:2.5
订单:黑咖啡, 加牛奶, 加糖
总价:2.8
订单:黑咖啡, 加糖, 加糖, 加牛奶
总价:3.0999999999999996
io中装饰模式的实现

1.测试类

public class IoTest {

    public static void main(String[] args) throws Exception {
        DataInputStream dis = null;
        try {
            dis = new DataInputStream(new BufferedInputStream(new FileInputStream("C:\\Users\\Lenovo\\Desktop\\test.txt")));
            byte[] bs = new byte[dis.available()];
            dis.read(bs);
            String content = new String(bs);
            System.out.println("文件内容为:" + content);
        } finally {
            dis.close();
        }
    }
}

2.结果
在这里插入图片描述

在这里插入图片描述
如图所示,它的结构和装饰模式的结构几乎是一样的。

  • InputStream 就相当于装饰模式中的Component。
  • 其实 FileInputStream、ObjectInputStream、StringBufferInputStream这几个对象是直接继承了InputSream,还有几个直接继承 - InputStream的对象,比如ByteArrayInputStream、PipedInputStream等。这些对象相当于装饰模式中的ConcreteComponent,是可以被装饰器装饰的对象。
  • FilterInputStream就相当于装饰模式中的Decorator ,而它的子类DataInputStream 、 BufferedInputStream , LineNumberInputStream和PushbackInputStream就相当于装饰模式中的 ConcreteDecorator了。另外FilterInputStream和它的子类对象的构造器,都是传入组件InputStream类型,这样就完全符合前面讲述的装饰器的结构了。

3.新增输入流装饰器

/**
 * @description:新增输入流装饰器
 */
public class AddInputStream extends FilterInputStream {

    public AddInputStream(InputStream in) {
        super(in);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        System.out.println("--->AddInputStream中进行自定义操作:"+ Arrays.toString(b));
        return super.read(b, off, len);
    }
}

4.修改测试类

public class IoTest {

    public static void main(String[] args) throws Exception {
        DataInputStream dis = null;
        try {
            dis = new DataInputStream(new AddInputStream(new BufferedInputStream(new FileInputStream("C:\\Users\\Lenovo\\Desktop\\test.txt"))));
            byte[] bs = new byte[dis.available()];
            dis.read(bs);
            String content = new String(bs);
            System.out.println("文件内容为:" + content);
        } finally {
            dis.close();
        }
    }
    
}

5.结果
在这里插入图片描述

看到新增的装饰器类生效了

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值