从零开始学设计模式(八):装饰器模式(Decorator Pattern)

简介: 在编程语言中一般有两种方式给一个类或者对象增加额外的行为或者功能:

定义


在编程语言中一般有两种方式给一个类或者对象增加额外的行为或者功能:


一种是继承:一个子类继承一个父类可以使得子类在拥有自身方法之外还能够同时拥有父类的方法。


还有一种是关联:即将一个类的对象嵌入到另一个对象中,并且由另一个对象决定是否调用嵌入对象的行为来扩展自己的功能,可见这种方式是动态的比继承更加灵活。而嵌入的对象就是这里要说的装饰器。


装饰器模式是继承关系的一个替代方案,在不必改变原类文件和原类使用的继承的情况  下,通过使用对象之间的关联关系来取代类之间的继承关系,从而能够动态的给一个对   象拓展一些额外的功能。它也是一种对象结构型模式。


组成部分


装饰器模式具有以下四个成员角色:


抽象构件角色(Project):给出一个接口,以规范准备接收附加责任的对象。


具体构件角色(Employe):定义一个将要接收附加责任的类。


装饰角色(Manager):持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口。


具体装饰角色(ManagerA、ManagerB):负责给构件对象贴上附加的责任。


例子


这里以手机的功能为例:


声明一个抽象构件角色手机的公共接口,并且具有拍照的功能:


public interface Phone {
    //手机具有拍照的功能
    void photo();
}
复制代码


再声明一个具体构件角色,


public class OldPhone implements Phone{
    @Override
    public void photo() {
        System.out.println("手机的拍照功能");
    }
}
复制代码


抽象装饰角色:


public class NewPhone implements  Phone{
    public OldPhone oldPhone;
    public NewPhone(OldPhone oldPhone) {
        this.oldPhone = oldPhone;
    }
    @Override
    public void photo() {
        System.out.println("手机的拍照功能");
    }
}
复制代码


具体装饰角色:


public class ConcreteDecorator extends NewPhone {
    public ConcreteDecorator(OldPhone oldPhone) {
        super(oldPhone);
    }
    public void photo() {
        super.photo();
        video();
    }
    public void video() {
        System.out.println("为手机增加额外的拍视频的功能");
    }
}
复制代码


测试:


public class Test {
    public static void main(String[] args) {
        Phone p = new OldPhone();
        p.photo();
        Phone phone = new ConcreteDecorator(new OldPhone());
        phone.photo();
    }
}
复制代码


结果:

27dbfb1b517842f884d0576c41ae39a3~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


通过例子可以看到,没有改变原来的OldPhone类,同时也没有定义他的子类而实现了Phone的扩展新增了拍视频的功能,这就是装饰器模式的作用。


装饰器模式优点:


1、装饰器模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。


2、可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。


3、通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。


4、具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”


装饰器模式缺点


1、使用装饰器模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类,增加了系统的复杂性


2、装饰器模式比继承更加灵活机动的特性,但同时也装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。


装饰器模式和继承的区别:


前面说了装饰器模式和继承机制都可以给一个类或者对象增加额外的行为或者功能,那么它们有什么区别:


继承比较于装饰器模式的优点在于代码结构清晰,而且实现简单;但是对于每一个的需要增强的类都要创建具体的子类来帮助其增强,这样会导致继承体系过于庞大。


而装饰器模式则可以动态的对对多个需要增强的类进行增强,同理需要维护需要增强的类的实例。进而使得代码稍微复杂。


装饰器模式与适配器模式的区别:


装饰器模式和适配器模式都是类结构设计模式,它们又有什么区别:


1、适配器模式主要用来兼容那些不能在一起工作的类,将它们转化为可以兼容目标接口,虽然也可以实现和装饰器一样的增加新职责,但是它的目的并不是这个。而装饰器模式主要是给被装饰的增加新职责的。


2、适配器模式是用新接口来调用原接口,原接口对新系统是不可见或者说不可用的。而装饰器模式原封不动的使用原接口,系统对装饰的对象也通过原接口来完成使用。


3、适配器模式是知道被适配者的详细情况的;而装饰器模式只知道其接口是什么,至于其具体类型(是基类还是其他派生类)只有在运行期间才知道。


使用场景


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


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


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


4、装饰器模式在java中的最著名的应用就是I/O 标准库的设计了。比如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。

目录
相关文章
|
3月前
|
设计模式 XML Java
【设计模式】装饰器模式(定义 | 特点 | Demo入门讲解)
【设计模式】装饰器模式(定义 | 特点 | Demo入门讲解)
41 0
|
20天前
|
设计模式 前端开发 JavaScript
前端必须掌握的设计模式——装饰器模式
装饰器模式是一种结构型设计模式,通过创建新类来包装原始对象,实现在不修改原有结构的前提下扩展新行为。其核心在于“组合”思想,使新功能可“即插即拔”。该模式具有解耦性、灵活性和动态性等特点,广泛应用于类的面向对象编程语言中,如JavaScript的注解和TypeScript的写法。示例中,通过装饰器模式为游戏角色动态添加装备,展示了其强大的扩展性和灵活性。
|
4月前
|
设计模式 Java
Java设计模式-装饰器模式(10)
Java设计模式-装饰器模式(10)
|
4月前
|
设计模式
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
这篇文章详细解释了工厂模式,包括简单工厂、工厂方法和抽象工厂三种类型。每种模式都通过代码示例展示了其应用场景和实现方法,并比较了它们之间的差异。简单工厂模式通过一个工厂类来创建各种产品;工厂方法模式通过定义一个创建对象的接口,由子类决定实例化哪个类;抽象工厂模式提供一个创建相关或依赖对象家族的接口,而不需要明确指定具体类。
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
|
4月前
|
设计模式 Java
设计模式--适配器模式 Adapter Pattern
这篇文章介绍了适配器模式,包括其基本介绍、工作原理以及类适配器模式、对象适配器模式和接口适配器模式三种实现方式。
|
7月前
|
设计模式
设计模式-05建造者模式(Builder Pattern)
设计模式-05建造者模式(Builder Pattern)
|
8月前
|
设计模式 安全 Java
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)
该文介绍了一种C++的编程技巧——奇异递归模板模式(CRTP),旨在让派生组件能继承基本组件的特定功能。通过示例展示了如何创建一个`Fighter`接口和`MmaFighter`类,其中`MmaFighter`及其子类如`MmaBantamweightFighter`和`MmaHeavyweightFighter`强制类型安全,确保相同重量级的拳手之间才能进行比赛。这种设计避免了不同重量级拳手间的错误匹配,编译时会报错。CRTP适用于处理类型冲突、参数化类方法和限制方法只对相同类型实例生效的情况。
|
7月前
|
设计模式 Java
Java设计模式:深入装饰器模式的三种写法(六)
Java设计模式:深入装饰器模式的三种写法(六)
|
7月前
|
设计模式 架构师 安全
设计模式第五讲-装饰器模式和代理模式详解
远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
284 0
|
3天前
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。