装饰器模式详解(UML、原理、使用、与代理模式的区别等)

来源:mikechen的互联网架构

设计模式是写出优秀程序的保障,与架构能力与阅读源码的能力息息相关,对程序员来说非常重要,非常值得深入学习。

前面,我们介绍了:

7 大设计原则(41张图解、2万多字)

5 大创建型设计模式总结,20 张图彻底掌握

今天我们主要介绍装饰器模式装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用。


01
  装饰模式的概述

装饰器模式(Decorator)是作为现有的类的一个包装,它允许向一个现有的对象添加新的功能,同时又不改变其结构,属于结构型模式。

举个例子:我们去买煎饼,可以在煎饼中添加鸡蛋、肉松或培根......。但不管添加什么,它仍然是一个煎饼。

除此之外,生活中还有很多类似装饰器模式的例子,例如火锅中可以添加毛肚、鹅肠、鸡毛菜等不同菜品......

02  装饰模式的结构

装饰器模式的 UML 类图:

装饰器模式的结构:

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


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


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


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

03
  装饰模式的作用

通常情况下,扩展一个类的功能会使用继承方式来实现。由于继承具有静态特征、耦合度高,随着扩展功能的增多,子类就会很膨胀。

装饰器模式是继承的有力补充,比继承灵活,可以在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用。

04
  装饰模式的实现

接下来,我们来看看装饰器模式的具体实现,仍然使用生活中的例子。

代码示例:用房子作为目标。

1)房子接口:House







/** * 目标接口:房子 */public interface House {    void output();}


2)具体的房子:ChenRuiHouse










/** * 房子实现类 */public class DonghaoHouse implements House {    @Override    public void output() {        System.out.println("这是陈睿的房子");    }}


3)具体的房子:MikeChenHouse










/** * 房子实现类 */public class MikeChenHouse implements House {    @Override    public void output() {        System.out.println("这是MikeChen的房子");    }}


4)装饰器:Decorator













public class Decorator implements House {    private House house;    public Decorator(House house){        this.house = house;    }    @Override    public void output() {        System.out.println("这是针对房子的前段装饰增强");        house.output();        System.out.println("这是针对房子的后段装饰增强");    }}


5)测试类








public class Clienter {    public static void main(String[] args) {        House ChenRuiHouse = new ChenRuiHouse();        House decorator = new Decorator(ChenRuiHouse);        decorator.output();    }}


结果:




这是针对房子的前段装饰增强这是陈睿的房子这是针对房子的后段装饰增强
05
  装饰模式的使用场景

装饰器模式的常见使用场景:

  • 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类;


  • 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现;


  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时。


在 Java 中,装饰器模式最著名的应用是 Java I/O 标准库的设计。

例如,

InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader等,它们都是抽象装饰类。

下面代码是为 FileReader 增加缓冲区,而采用的装饰类 BufferedReader 的例子:



BufferedReader in = new BufferedReader(new FileReader("filename.txt"));String s = in.readLine();
06
  装饰模式的优缺点

装饰器模式的优点:

  • 装饰类和被装饰类可以独立发展,不会相互耦合,互相都不用知道对方的存在;


  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用;


  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果。


装饰器模式的缺点:

装饰器模式会增加许多子类,多层装饰比较复杂,过度使用会增加程序的复杂性,不利于我们调试。

07
  装饰器模式与代理模式的区别

装饰器模式与代理模式的区别,主要体现在概念及使用等方面。

1) 概念方面

  • 代理是全权代理,目标根本不对外,全部由代理类来完成;


  • 装饰是增强,是辅助,目标仍然可以自行对外提供服务,装饰器只起增强作用。


代理模式:













public class Proxy implements House {    private House house;    public Decorator(){        this.house = new DonghaoHouse();    }    @Override    public void output() {        System.out.println("这是针对目标的前段增强");        house.output();        System.out.println("这是针对目标的后段增强");    }}


代理模式针对的目标实现类是固定的,代理中持有的目标实例是自己创建的。

装饰模式:













public class Decorator implements House {    private House house;    public Decorator(House house){        this.house = house;    }    @Override    public void output() {        System.out.println("这是针对房子的前段装饰增强");        house.output();        System.out.println("这是针对房子的后段装饰增强");    }}


装饰器模式可以随意指定目标实现类,即目标是可以自由扩展的,装饰器中持有的目标实例是从构造器传入的。

2) 使用场景

    两者都是对类的方法进行扩展,但使用场景不同:

  • 装饰器模式


    强调的是增强自身,在被装饰之后,能够在被增强的类上使用增强后的功能。在增强后,你还是你,只不过能力更强了而已。

  • 代理模式


    强调要让别人帮你去做一些本身与你业务没有太多关系的职责(记录日志、设置缓存)。


    代理模式是为了实现对象的控制,因为被代理的对象往往难以直接获得,或者是其内部不想暴露出来。


    装饰器模式应当为所装饰的对象提供增强功能,而代理模式对所代理对象的使用施加控制,并不提供对象本身的增强功能。


总结


通过本文,我们了解并掌握了装饰器模式,包括装饰器模式的概念、作用、结构、使用场景、优缺点及应用等。

设计模式的重要性就不多说了,只有我们了解每一种设计模式,实际应用时才能够合理选型,避免因强行使用设计模式、让代码更加不好维护的情况出现。


请使用浏览器的分享功能分享到微信等