装饰模式(Decorator Pattern)是一种比较常见的模式,动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活(Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality) 装饰模式的通用类图如图: ![](https://hackmd.io/_uploads/H1mq89Aoh.png) - Component 是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象 - ConcreteComponent 是被装饰的对象,是最核心、最原始、最基本的接口或抽象类的实现 - Decorator 装饰角色抽象类,实现或继承 Component,它里面不一定有抽象的方法,在它的属性里必然有一个 private 变量指向 Component 对象。 - ConcreteDecorator 具体的装饰角色,可以用来在 Component 对象的基础上添加新的东西 举个例子,球队评估是否要和某个球星签约,首先要找到球探做做调查,然后输出球探报告给教练和管理层,教练和管理层需要审阅报告,并对是否签约做出批示。 球探报告用 Java 代码表示如下: ```java interface Report { // 通过 Report 接口将 ScoutingReport 对象的访问权到处给 Decorator public void report(); } class ScoutingReport implements Report { // 我的成绩单 public void report() { // 球探报告的内容是这个样子的 System.out.println("----关于XX队YY的球探报告----"); System.out.println(":"); System.out.println(" ......"); System.out.println(" 位置:PF/SF"); System.out.println(" 身高:6 ft 9 in"); System.out.println(" 优点:同位置上精英级运球和低位单打能力,攻防转换快,弹跳好,防守不失位"); System.out.println(" 缺点:中远投命中率低,臂展太短"); System.out.println(" ......."); System.out.println(" 球探签名为:RP"); } } ``` - ScoutingReport 就是被包装的对象 - Report 接口将 ScoutingReport 对象的访问权导出给 Decorator 教练和球队经理的审阅和批示则是用装饰模式实现的功能,通过调用 super.report() 审阅报告,同时附上自己的批示,教练和球队经理可以自定义自己审阅报告和批示的顺序,本质上这还是模板方法模式: ```java abstract class Decorator implements Report { private Report sr; // 构造函数,传递报告过来 public Decorator(Report sr) { this.sr = sr; } // 报告被审阅 @Override public void report() { this.sr.report(); } } class ManagerReview extends Decorator { // 构造函数 public ManagerReview(Report sr) { super(sr); } // 球队经理的批示内容 private void review() { System.out.println("球队经理:这球员我签了..."); } // 在 Title 批示 @Override public void report() { // 先批示,再审阅报告 this.review(); super.report(); } } class CoachReview extends Decorator { // 构造函数 public CoachReview(Report sr) { super(sr); } // 教练的批示内容 private void review() { System.out.println("教练意见:这球员我要了..."); } // 在报告 Tail 做出批示 @Override public void report() { // 先审阅报告,再批示 super.report(); this.review(); } } ``` 从场景代码中可以看到装饰模式的封装方式 ```java public class Client { public static void main(String[] args) { // 把成绩单拿过来 Report sr; // 原装的成绩单 sr = new ScoutingReport(); // 又加了成绩排名的说明 sr = new CoachReview(sr); // 加了最高分说明的成绩单 sr = new ManagerReview(sr); // 最终的报告内容 sr.report(); } } ``` ## 装饰模式的优点 装饰类和被装饰类可以独立发展,而不会相互耦合。换句话说,Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的构件。 装饰模式是继承关系的一个替代方案。不管用 Decorator 类装饰多少层,返回的对象还是 Component ,实现的还是is-a的关系,解决了类膨胀的问题,使代码容易维护。 装饰模式可以动态地扩展一个实现类的功能,提升了可扩展性。 ## 装饰模式的缺点 切记不能进行多层装饰,因为太复杂,debug 工作量大。 ## 装饰模式的使用场景 需要扩展一个类的功能,或给一个类增加附加功能。 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。