介绍
装饰模式指的是在不必改变原类文件和使用继承的情况下,通过组合的方式动态地给对象增加新功能。
装饰者模式通过将对象包装在一个装饰者类中,该装饰者类具有与原始对象相同的接口,并且可以透明地将客户端的请求传递给原始对象。同时,装饰者类可以在传递请求之前或之后添加自己的行为,从而实现对对象的动态扩展。
装饰者模式的核心思想是通过组合而非继承来实现对对象的扩展。通过将对象包装在装饰者类中,可以在运行时动态地添加、删除或修改对象的行为,而无需修改原始对象的结构。
模式角色
(1) 抽象组件(Component)
抽象组件通常是一个接口或者抽象类,它定义了一些基本的操作,装饰器和具体组件都要实现这个接口或者抽象类。
(2) 具体组件(Concrete Component)
是实现抽象组件接口的一个具体类,它是被装饰的原始对象,装饰器将会给它动态地添加职责。
(3) 抽象装饰器(Decorator)
它包含一个抽象组件的引用,并且定义了与抽象组件相同的接口的类。在抽象装饰器中,可以定义一些与具体装饰器共享的方法和属性。
(4) 具体装饰器(Concrete Decorator)
是实现抽象装饰器接口的一个具体类,它包含了一个抽象组件的引用,并且动态地给这个抽象组件添加职责。具体装饰器通常包含一个构造函数,用来传入一个抽象组件对象,以便给它添加职责。
具体组件就是原类,我们需要通过具体装饰器为它添加新功能。
代码示例
参考:《Head First 设计模式》
实现的功能:定义一个抽象Coffee类,具体的咖啡实现类为DarkRoast,其它配料如Whip、Mocha作为装饰类。
使用代码实现后,一共有6个类:
| 类 | 功能 |
|---|---|
| Coffee | 抽象组件(抽象被装饰类),咖啡抽象类 |
| DarkRoast | 具体组件(具体被装饰类),具体的咖啡种类 |
| CondimentDecorator | 抽象装饰类,由Whip和Mocha继承该类 |
| Whip | 具体装饰类,给咖啡加入奶泡 |
| Mocha | 具体装饰类,给咖啡加入摩卡 |
| MakeCoffee | 测试类 |
具体代码如下:
package com.Structural;
/**
* 装饰者(Decorator)模式示例
*/
public class MakeCoffee {
public static void main(String[] args) {
// 原始的DarkRoast咖啡
Coffee coffee1 = new DarkRoast();
System.out.println(coffee1.getDescription()
+ " $" + coffee1.cost());
// 加两份Mocha和一份Whip
Coffee coffee2 = new DarkRoast();
coffee2 = new Mocha(coffee2);
coffee2 = new Mocha(coffee2);
coffee2 = new Whip(coffee2);
System.out.println(coffee2.getDescription()
+ " $" + coffee2.cost());
// 加一份Mocha和一份Whip
Coffee coffee3 = new DarkRoast();
coffee3 = new Mocha(coffee3);
coffee3 = new Whip(coffee3);
System.out.println(coffee3.getDescription()
+ " $" + coffee3.cost());
}
}
/**
* 抽象组件(抽象被装饰类)——基本的咖啡类
* 作用:定义接口,装饰器和具体组件都要实现这个接口或者抽象类。
*/
abstract class Coffee {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
/**
* 具体组件(具体被装饰类)——DarkRoast(焦炒)表示一种具体的咖啡,也是被装饰者
* 它继承抽象的被装饰者类,并实现其中的抽象方法。
*/
class DarkRoast extends Coffee {
public DarkRoast() {
description = "Dark Roast Coffee";
}
public double cost() {
return 0.99;
}
}
/**
* 抽象装饰者类——CondimentDecorator(调味品装饰类)
* 1.抽象装饰者类中继承该抽象类以保持接口规范
* 2.包含该抽象类的引用以通过多态的方式对多种被装饰者类进行装饰。
*/
abstract class CondimentDecorator extends Coffee {
Coffee coffee;
}
/**
* 装饰者类——奶泡
* 给具体被装饰类DarkRoast加入奶泡
*/
class Whip extends CondimentDecorator {
public Whip(Coffee coffee) {
this.coffee = coffee;
}
public String getDescription() {
return coffee.getDescription() + ", Whip";
}
public double cost() {
return 0.10 + coffee.cost();
}
}
/**
* 装饰者类——摩卡
* 给具体被装饰类DarkRoast加入摩卡
*/
class Mocha extends CondimentDecorator {
public Mocha(Coffee coffee) {
this.coffee = coffee;
}
public String getDescription() {
return coffee.getDescription() + ", Mocha";
}
public double cost() {
return 0.20 + coffee.cost();
}
}
输出为:
Dark Roast Coffee $0.99
Dark Roast Coffee, Mocha, Mocha, Whip $1.49
Dark Roast Coffee, Mocha, Whip $1.29
应用场景
装饰者模式使用一种类似于递归的方式,不断地包装原始对象,从而实现对对象的动态扩展。下面是几个常见的装饰者模式使用场景。
(1) GUI 界面中的装饰器
在 GUI 界面中,可以使用装饰器模式来动态地添加界面组件。例如,可以使用一个基本的文本框组件作为原始对象,然后通过装饰器来添加边框、滚动条、背景等效果。
(2) Java I/O 流中的装饰器
Java I/O 流中的输入流和输出流都是以装饰者模式实现的。例如,可以使用一个基本的文件输入流作为原始对象,然后通过装饰器来添加缓冲、转换、加密等功能。
(3) 订单系统中的装饰器
在订单系统中,可以使用装饰者模式来实现不同的订单处理方式。例如,可以使用一个基本的订单处理对象作为原始对象,然后通过装饰器来添加支付、发货、退款等功能。
(4) 缓存系统中的装饰器
在缓存系统中,可以使用装饰者模式来实现不同的缓存策略。例如,可以使用一个基本的缓存对象作为原始对象,然后通过装饰器来添加 LRU 策略、过期策略、分布式策略等。
总之,装饰者模式适用于需要动态地扩展对象功能的场景,它可以避免使用继承来扩展对象功能所带来的类爆炸问题。同时,装饰者模式也使得对象功能的扩展更加灵活、可配置和可组合。
Android中的应用-Context
| 类 | 角色 |
|---|---|
| Context | 抽象组件(抽象被装饰者),它给出了抽象接口 |
| ContextImpl | 具体组件(具体被装饰者),定义一个具体实现的类 |
| ContexWrapper | 抽象装饰类,持有一个ContextImpl对象的实例,并实现一个与抽象构件接口一致的接口 |
| Activity,Service等 | 具体装饰类,负责给ContextImpl添加上额外的功能 |
以Activity为例,看装饰器模式在Context中的应用。
Activity创建过程中,会执行到AT.performLaunchActivity(),会在这里创建当前Activity的Context实例(ContextImpl对象),该Context实例最终通过createActivityContext()创建并返回,调用栈如下:
ActivityThread.performLaunchActivity() ->
ActivityThread.createBaseContextForActivity() ->
ContextImpl.createActivityContext()
创建具体组件ContextImpl之后,要将其添加到装饰器ContexWrapper中,调用栈如下:
ActivityThread.performLaunchActivity() ->
Activity.attach() ->
Activity.attachBaseContext ->
ContextThemeWrapper.attachBaseContext() ->
ContextWrapper.attachBaseContext()
总的流程为:
- 创建具体被装饰类ContextImpl;
- 创建具体装饰类Activity,继承链为Activity->ContextThemeWrapper->ContextWrapper;
- 将Activity和ContextImpl关联起来,即Activity持有ContextImpl的引用。