定义

装饰器模式(Decorator Pattern) 也称为包装模式(Wrapper Pattern) 是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。装饰器模式的核心是功能扩展,使用装饰器模式可以透明且动态地扩展类的功能。

本章代码:小麻雀icknn/设计模式练习 – Gitee.com

模式特点

  • 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
  • 装饰对象包含一个真实对象的引用 (reference)
  • 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
  • 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行(4)时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展

装饰设计模式角色

  • 抽象组件Component类。
  • 组件具体实现ConcreteComponent类。也是被装饰的对象
  • 抽象装饰类Decorator,内部持有一个组件对象的引用,职责就是装饰ConcreteComponent类。之所以是抽象的,就是为了方便不同的装饰“风格”子类的自定义实现。
  • 具体装饰类ConcreteDecorator。

UML类图

用到的基本概念:抽象类

什么是抽象类?

  • 类和类之间具有共同特征,将这些共同特征抽取出来,形成的就是抽象类
  • 如果一个类没有足够的信息来描述一个具体的对象,这个类就是抽象类。
  • 因为类本身就是不存在的,所以抽象无法创建对象,也就是无法实例化

问题:既然抽象类是无法创建对象,无法实例化,那是用来干嘛的???

抽象类是用来被子类继承的。

抽象类属于什么类型?

  • 抽象类属于引用数据类型

例子:

锤子抽象类

package com.study.main;/** * 锤子抽象类 */public abstract class Hammer {String name;String price;String decribe;public Hammer() {}abstract String getPrice();abstract String getDecribe();}

普通锤子实例类

package com.study.main;/** ** 锤子实例 */public class BaseHammer extends Hammer{String price;String describe;public BaseHammer(String price,String describe) {super();this.price = price;this.describe = describe;}@OverrideString getPrice() {return this.price;}@OverrideString getDecribe() {return this.describe;}}

锤子装饰类

package com.study.main;/** * 锤子装饰类 */public class HammerDecorator extends Hammer{Hammer hammer;public HammerDecorator(Hammer hammer) {super();this.hammer = hammer;}@OverrideString getPrice() {return this.hammer.getPrice();}@OverrideString getDecribe() {return this.hammer.getDecribe();}}

装饰1实现类

package com.study.main;/** * 装饰1 */public class IronHammer extends HammerDecorator{public IronHammer(Hammer hammer) {super(hammer);}@OverrideString getDecribe() {return super.getDecribe() + ",这是一个铁做的锤子";}@OverrideString getPrice() {return super.getPrice() + "+ 20元";}}

装饰2实现类

package com.study.main;/** * 装饰2 */public class StoneHammer extends HammerDecorator{public StoneHammer(Hammer hammer) {super(hammer);}@OverrideString getPrice() {return super.getPrice() + "+ 10元";}@OverrideString getDecribe() {return super.getDecribe() + ",这是一个由石头构成的";}}

测试&运行

package com.study.main;public class DecoratorMain {public static void main(String[] args) {Hammer hammer = new BaseHammer("10", "锤子描述:");IronHammer ironHammer = new IronHammer(hammer);StoneHammer stoneHammer = new StoneHammer(hammer);System.out.println(ironHammer.getDecribe());System.out.println(ironHammer.getPrice());System.out.println(stoneHammer.getDecribe());System.out.println(stoneHammer.getPrice());}}

装饰模式在源码中的应用

JAVA中

Mybatis中

使用场景

一般来讲装饰模式常用的使用场景有以下几种

  • 快速动态扩展和撤销一个类的功能场景。 比如,有的场景下对 AP! 接口的安全性要求较高,那么就可以使用装饰模式对传输的字符串数据进行压缩或加密。如果安全性要求不高,则可以不使用。
  • 可以通过顺序组合包装的方式来附加扩张功能的场景。 比如,加解密的装饰器外层可以包装压缩解压缩的装饰器,而压缩解压缩装饰器外层又可以包装特殊字符的筛选过滤的装饰器等。
  • 不支持继承扩展类的场景。 比如,使用 final 关键字的类,或者系统中存在大量通过继承产生的子类。
  • 在现实中有一个很形象的关于装饰器使用场景的例子,那就是单反相机镜头前的滤镜。用过单反相机的同学应该知道,不加滤镜其实不会影响拍照,而滤镜实际上就是一个装饰器,滤镜上又可以加滤镜,这样就做到了不改变镜头而又给镜头增加了附加功能.
  • 总结来说,装饰模式适用于一个通用功能需要做扩展而又不想继承原有类的场景,同时还适合一些通过顺序排列组合就能完成扩展的场景

装饰模式优缺点

优点:

  • 装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象 扩展功能,即插即用。
  • 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
  • 装饰者完全遵守开闭原则。

缺点:

  • 会出现更多的代码,更多的类,增加程序复杂性。
  • 动态装饰以及多层装饰时会更加复杂。