1.概述
- 动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)。 ——《设计模式》GoF
-
1.1动机
- 在某些情况下我们可能会"过度地使用继承来扩展对象的功能"由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。- 如何使"对象功能的扩展"能够根据需要来动态地实现 ? 同时避免"扩展功能的增多"带来的子类膨胀问题, 从而使得任何"功能扩展变化""所导致的影响将为最低 ?
1.2结构
- 
2.要点总结
- 通过采用组合而非继承的手法, Decorator模式 实现了在运行时动态扩展对象功能的能力. 而且可以根据需要扩展多个功能. 避免了使用继承带来的 “灵活性差” 和 “多子类衍生问题”
- Decorator类 在接口上表现为 is-a Component 的继承关系, 即Decorator类 继承了 Component类所具有的接口. 但又在实现上表现为 has-a Component 的组合关系, 即 Decorator类 又使用了另一个Component类
- Decorator模式的 目的并非解决 “多子类衍生的多继承”问题, 该模式应用的要点在于解决 “主体类在多个方向上的扩展功能” — 是为”装饰”的含义
3.案例
星巴克咖啡订单项目:
类图
代码
// 被装饰者: 抽象主体, 缓冲层, 具体主体 ```java /**
- @description: TODO 被装饰的抽象主体, 缓冲层, 具体主体 */ public abstract class Drink {
public String description; // 描述 private float price = 0.0F;// 价格
/**
- @description: 计算费用的抽象方法,供子类实现
- @return: float 当前(主体+装饰者)的状态(价格) */ public abstract float cost();
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public float getPrice() { return price; }
public void setPrice(float price) { this.price = price; } }
/**
@description: 缓冲层 */ class Coffee extends Drink{
@Override public float cost() {
return super.getPrice();
} }
// ————— 以下是具体主类 ———————-
class ShortBLack extends Coffee{ ShortBLack(){ setDescription(“shortBlack 4.0”); setPrice(4.0F); } }
class Espresso extends Coffee{ Espresso(){ setDescription(“espresso 5.0”); setPrice(5.0F); } }
class LongBlack extends Coffee{ public LongBlack(){ setDescription(“longBlack 6.0”); setPrice(6.0F); } }
/**
- @description: 待新增的类, 供后续测试 */ class Decaf{
}
- // 装饰者: 装饰者类及其子类```java/*** @description: TODO 装饰者(继承结构)*/public class Decorator extends Drink{private Drink obj;public Decorator(Drink obj){ // 组合this.obj = obj;}@Overridepublic float cost() {// 操作 装饰者自身 + 具体主体return super.getPrice() + obj.cost();}@Overridepublic String getDescription() {// 查看验证return description + " " + getPrice() + " && " + obj.getDescription();}}// -------------- 以下是具体子装饰类 --------------class Chocolate extends Decorator{public Chocolate(Drink obj){super(obj);setDescription(" Chocolate ");setPrice(3.0F);}}class Milk extends Decorator{public Milk(Drink obj){super(obj);setDescription(" Milk ");setPrice(4.0F);}}class Soy extends Decorator{Soy(Drink obj){super(obj);setDescription(" Soy ");setPrice(5.0F);}}
- // 测试类 ```java
public class CoffeeBar {
public static void main(String[] args) {// TODO Auto-generated method stub// 装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack// 1. 点一份 LongBlackDrink order = new LongBlack();System.out.println("费用1=" + order.cost());System.out.println("描述=" + order.getDescription());// 2. order 加入一份牛奶order = new Milk(order);System.out.println("order 加入一份牛奶 费用 =" + order.cost());System.out.println("order 加入一份牛奶 描述 = " + order.getDescription());// 3. order 加入一份巧克力order = new Chocolate(order);System.out.println("order 加入一份牛奶 加入一份巧克力 费用 = " + order.cost());System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDescription());// 3. order 加入一份巧克力order = new Chocolate(order);System.out.println("order 加入一份牛奶 加入2份巧克力 费用 =" + order.cost());System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDescription());System.out.println("===========================");
/* Drink order2 = new DeCaf();
System.out.println("order2 无因咖啡 费用 =" + order2.cost());System.out.println("order2 无因咖啡 描述 = " + order2.getDescription());order2 = new Milk(order2);System.out.println("order2 无因咖啡 加入一份牛奶 费用 =" + order2.cost());System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDescription());*/}
}
- // 打印
费用1=6.0 描述=longBlack 6.0 order 加入一份牛奶 费用 =10.0 order 加入一份牛奶 描述 = Milk 4.0 && longBlack 6.0 order 加入一份牛奶 加入一份巧克力 费用 = 13.0 order 加入一份牛奶 加入一份巧克力 描述 = Chocolate 3.0 && Milk 4.0 && longBlack 6.0 order 加入一份牛奶 加入2份巧克力 费用 =16.0
order 加入一份牛奶 加入2份巧克力 描述 = Chocolate 3.0 && Chocolate 3.0 && Milk 4.0 && longBlack 6.0
<a name="wXvRB"></a># 5.经典使用---<a name="b5SbS"></a>## 5.1JDK中IO流<a name="uoO3g"></a>#### 5.1.1两种设计方案的对比- 使用继承的硬编码(对继承的不良使用)- 类数量: 1+n+n*m!/2- - 使用装饰者模式- 类数量: 1+n+1+m- <a name="O0Vt7"></a>#### 5.1.2结构<a name="A3nUu"></a>#### 5.1.3伪码```cpp//扩展的操作: 在基础流上的加密操作class CryptoStream: public Stream {Stream* stream;//...public:CryptoStream(Stream* stm):stream(stm){}virtual char Read(int number){//额外的加密操作...stream->Read(number);//读文件流}virtual void Seek(int position){//额外的加密操作...stream::Seek(position);//定位文件流//额外的加密操作...}virtual void Write(byte data){//额外的加密操作...stream::Write(data);//写文件流//额外的加密操作...}};
5.1.4分析及总结
如上的C++扩展的加密流的伪码分析:1.继承自Stream, 以之加密的操作能配合Stream抽象基类的读写操作(后续由具体的网络流/缓冲流实现)2.参数化Stream, 接收对应的实际需要加密的流类型(如上具体的网络流/缓冲流), 以抽象化加密接口.否则: 对应的网络流/缓冲流, 都需要一个具体的 "加密网络流"/"加密缓冲流", 导致类爆炸3.由编译时装配, 改善成运行时 (组合的方式)装配4.(实例教程中)由于所有的扩展类都需要一个Stream成员变量, 所以可以将这个通有Stream抽象到上层.而若抽象到基类Stream中, 基类本身并不需要Stream变量, 不符合单一职责.故可新建一个二者之间的缓冲层来存放这个子类通用的Stream变量.5.Java中:InputStream基类的直接实现子类FilterInputStream, 即如上抽象层作用.其包含一个成员变量{ protected volatile InputStream in; }JDK对 FilterInputStream类 描述:FilterInputStream包含一些其他输入流,它用作其基本的数据源,可能会沿途转换数据或提供附加功能。FilterInputStream本身简单地覆盖了所有InputStream的方法, InputStream版本将所有请求传递给包含的输入流。FilterInputStream可以进一步覆盖这些方法中的一些,并且还可以提供附加的方法和领域。总结:FilterInputStream类 即是父类装饰者, 其具体子类(BufferedInputStream等)即具体装饰者, 扩展了缓冲/BASE编码等功能.FileInputStream类 即父类组件, 其具体子类即具体组件.(虽然装饰者基类直接装饰的变量类型是InputStream, 但通常都操作 FileInputStream )(对于OutputStream/Reader/Write同理. Reader基类的直接抽象子类FilterReader, 也包含了一个 Reader 类型的成员变量)
案例对比IO源码分析://1. InputStream是抽象类,类似我们前面讲的Drink//2. FileInputStream是InputStream 子类,类似我们前面的DeCaf, LongBlack//3. FilterInputStream是InputStream 子类:类似我们前面的Decorator 修饰者//4. DataInputStream是FilterInputStream 子类,具体的修饰者,类似前面的Milk, Soy等//5. FilterInputStream类有protected volatile InputStream in;即含被装饰者//6.分析得出在 JDK-IO 体系中,就是使用装饰者模式
x.其它
笔记

