设计模式11之装饰模式

背景

我们在开发过程中,如果我们想使用存在的轮子,但是轮子的有些功能不能满足我们。我们需要对轮子新增想要的功能。好的设计方式是不改变轮子的结构,动态的扩展功能。装饰模式就是这样的一种设计方式。

什么是装饰模式

装饰模式(Decorator Pattern)是一种比较常见的模式,其定义如下:Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)

是不是觉得跟代理模式有些像呢?对代理模式感兴趣的可以看看我的这篇文章:

👉设计模式6之代理模式

👉面试必问的动态代理,我们来看看它们的源码

 

 

别急,我们继续往下看。

装饰模式的主要组成要素为:

  • 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。

  • 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。

  • 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。

  • 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

要素之间的关系如下:

装饰模式

示例代码

Component

public interface Component {
    void operation();
}

ConcreteComponent

public class ConcreteComponent implements Component {

    public ConcreteComponent() {
        System.out.println("创建具体构件角色");
    }

    @Override
    public void operation() {
        System.out.println("调用具体构件角色的方法operation()");
    }
}

Decorator

public class Decorator implements Component {
    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        component.operation();
    }
}

ConcreteDecorator

public class ConcreteDecorator extends Decorator {

   public ConcreteDecorator(Component component) {
      super(component);
   }

   @Override
   public void operation() {
      super.operation();
      addedFunction();
   }

   public void addedFunction() {
      System.out.println("为具体构件角色增加额外的功能addedFunction()");
   }
}

测试代码如下:

public class DecoratorTest {
    public static void main(String[] args){
        Component p = new ConcreteComponent();
        p.operation();
        System.out.println("------------------");
        Component concreteDecorator = new ConcreteDecorator(p);
        concreteDecorator.operation();
    }
}

如上代码,我们想使用具体构件(即文章开头的轮子)的operation(),并且想对operation()方法增加一些功能,我们构建了一个装饰对象,这个对象包裹这具体构件,最后我们调用了ConcreteDecorator#operation(),这个方法就是增强后的方法。

测试结果:

-------------------
调用具体构件角色的方法operation()
------------------
调用具体构件角色的方法operation()
为具体构件角色增加额外的功能addedFunction()

我们对operation()方法增加了功能:

为具体构件角色增加额外的功能addedFunction()

关于装饰模式的思考

如果我们需要对当前类添加额外的职责,但是又不能通过生成当前类的子类去扩充时,就可以使用装饰模式了。

装饰者模式的优势在于可以动态扩展新功能,也可以动态撤销新功能,还可以将多组功能排列组合生成新功能。

其实Java中的InputStream的子类 FilterInputStreamOutputStream 的子类 FilterOutputStream 等,它们都是抽象装饰类。

BufferedReader in=new BufferedReader(new FileReader("lvshen_av.txt));
String s=in.readLine();

如上代码就使用了装饰模式。

对于构件Component和装饰者Decorator来说,它们之间不相互耦合。装饰者是从外部扩展构件的功能,不需要知道构件的具体结构,不需要改变构件。

通俗点就是:

史塔克穿上钢铁侠战甲就变成钢铁侠,同样他的女友小辣椒也能穿上钢铁侠战甲变成钢铁侠。钢铁侠战甲不关注谁穿上了它。不同的人穿上就成了不同的钢铁侠。

但是只要注意不要多层装饰,这样会显得系统很复杂。

例如,史塔克穿上钢铁侠战甲成为钢铁侠,钢铁侠穿上反浩克战甲。虽然力量变强了,但是操作难度也变高了。

关于装饰模式与代理模式

两者都是对功能增强。面试会经常问这两点区别。

我认为,装饰器模式关注于在一个对象上动态的添加方法,代理模式关注于控制对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

你还可以这样理解:

  • 装饰模式中史塔克可以穿上钢铁侠战甲,女友小辣椒也能装上钢铁侠战甲。

  • 代理模式中史塔克和钢铁侠战甲已经绑死了,战甲也焊死了,小辣椒没法穿这套战甲了。

如图,左边为代理模式,右边为装饰模式。

左边为代理模式的调用,这里我们不需要传入目标类,目标类对象在代理类内部生成。

右边为装饰模式的调用,目标类对象需要在这里传入装饰类中。

至于优缺点,大家可以自己思考下它们的使用场景。

往期推荐

扫码二维码,获取更多精彩。或微信搜Lvshen_9,可后台回复获取资料

1.回复"java" 获取java电子书;

2.回复"python"获取python电子书;

3.回复"算法"获取算法电子书;

4.回复"大数据"获取大数据电子书;

5.回复"spring"获取SpringBoot的学习视频。

6.回复"面试"获取一线大厂面试资料

7.回复"进阶之路"获取Java进阶之路的思维导图

8.回复"手册"获取阿里巴巴Java开发手册(嵩山终极版)

9.回复"总结"获取Java后端面试经验总结PDF版

10.回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF)

11.回复"并发导图"获取Java并发编程思维导图(xmind终极版)

另:点击【我的福利】有更多惊喜哦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值