一、概念
装饰模式(Decorator)又称包装(Wrapper)模式。装饰模式以对客户端透明的方式扩展对象的功能(动态地给一个对象添加一些额外的职责),是继承的一种替代方案(比生成子类更为灵活)。
UML 类图:
在装饰模式中的角色有:
● 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
● 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
● 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
● 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
Component 是定义一个对象接口,可以给这些对象动态地添加职责。ConcreteComponent 是定义了一个具体的对象,也可以给这个对象添加一些职责。Decorator,装饰抽象类,继承了Component,从外类来扩展Component 类的功能,但对于Component 来说,是无须知道Decorator 的存在的。至于ConcreteDecorator 就是具体的装饰对象,起到给Component 添加职责的功能。
二、实例
抽象构建角色
public interface Star { void show(); }
具体构建角色
public class FootballStar implements Star { @Override public void show() { System.out.println("足球明星表演踢球"); } }
装饰角色
public class Decorator implements Star { protected Star star; // 设置Component public Decorator(Star star) { this.star = star; } @Override public void show() { // 委派给构建 star.show(); } }
具体装饰角色
public class ConcreteDecoratorA extends Decorator { // 父类已经定义了一个有参的构造函数并且父类中没有默认的无参构造方法,此时编译器不会为你调用默认的构造函数 // 当子类继承时,必须在自己的构造函数显式调用父类的构造函数,自己才能确保子类在初始化前父类会被实例化 public ConcreteDecoratorA(Star star) { // 父类的构造方法不用继承,我们可以用super来调用 // 隐式的超级构造函数Decorator() 是未定义的。必须显示调用另一个构造函数(自己写的) super(star); } public void show() { // 1.首先运行原Component 的show(), super.show(); // 写相关的业务代码 // 2.再执行本类中的功能,如run(),相当于对Component 进行了装饰 run(); System.out.println("具体的装饰器类ConcreteDecoratorA。"); } public void run() { System.out.println("A 跑步!"); } } ----------------------------------------------------------- public class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Star star) { super(star); } private String bStr; public void show() { super.show(); // 额外职责 this.bStr = "123"; System.out.println(bStr); System.out.println("具体的装饰器类ConcreteDecoratorB。"); } }
测试方法
public class DecoratorTest { public static void main(String[] args) { // (1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。 // (2) 装饰对象包含一个真实对象的引用(reference) // (3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。 // (4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。 // 在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。 // 继承不能做到这一点,继承的功能是静态的,不能动态增删。 // 1. 首先用FootballStar 实例化对象 fs FootballStar fs = new FootballStar(); // 2. 然后用ConcreteDecoratorA 的实例化对象a 来包装 fs(装饰对象包含真是对象的引用) ConcreteDecoratorA a = new ConcreteDecoratorA(fs); // 3. 再用ConcreteDecoratorB 的实例化对象b 来包装a ConcreteDecoratorB b = new ConcreteDecoratorB(a); // 最终执行 b.show() b.show(); } }
结果
三、总结
动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。
扩展:
1.继承其实也可以直接子类调用父类的方法,也没有修改父类内容实现代码,那为什么选择装饰者模式,而不是继承?
当业务需求增加时,不断的扩展子类会是项目变得臃肿,此时通过装饰者模式可以精简代码,统一放在装饰角色中,再分别增加业务扩展。 装饰模式在Java语言中的最著名的应用莫过于Java I/O标准库的设计了。
2.继承和装饰者之间的区别