7、装饰者模式

装饰者模式

从我们之前学到的知识中, 已经知道扩展一个类的方法是为他派生新的子类, 也就是通过继承来扩展功能.

然而, 使用继承是静态的, 在编译的时候就已经决定了子类的行为, 我们不便于控制增加行为的方式和时机.

而装饰者模式可以动态地将责任附加到对象上, 若要扩展对象, 装饰者模式提供了比继承更弹性的替代方案.

使用场景:对象由主体+许多可选的部件或者功能构成,使用继承或者接口会产生很多类,且很难扩展。例如,现在需要一个汉堡,主体是鸡腿堡,可以选择添加生菜、酱、辣椒等等许多其他的配料,这种情况下就可以使用装饰者模式。

装饰模式的UML图:

  ●  抽象构件(Component)类:给出一个抽象接口, 以规范准备接收附加责任的对象.

  ●  具体构件(ConcreteComponent)类:被修饰的者, 是定义了一个具体的对象, 可以给这个对象添加一些职责(也就是装饰它).

  ●  装饰(Decorator)类:抽象装饰类, 维护一个构件(Component)对象的实例, 定义一个与抽象构件接口一致的接口.

  ●  具体装饰(ConcreteDecorator)类:具体装饰类, 负责给构件对象"贴上"附加的责任.

它的C++模型如下(技术有限, 可能实现的有问题, 欢迎指正):

#include <iostream>
using namespace std;

class Component {
public:
    virtual char* Operation() = 0;

protected:
    char m_description[256];
};

class ConcreateComponent
    : public Component {
public:
    ConcreateComponent() {
        strcpy_s(m_description, 255, "初始状态");
    }

    char* Operation() {
        return m_description;
    }
};

class Decorator
    : public Component {
public:
    Decorator(Component* pComponent) {
        m_pComponent = pComponent;
    }

    virtual char* Operation() = 0;

protected:
    Component* m_pComponent;
};

class ConcreteDecoratorA
    : public Decorator {
public:
    ConcreteDecoratorA(Component* pComponent) : Decorator(pComponent) {
        strcat_s(m_pComponent->Operation(), 255, " --> 装饰了 A ‘饰品‘");
    }

    char* Operation() {
        return m_pComponent->Operation();
    }
};

class ConcreteDecoratorB
    : public Decorator {
public:
    ConcreteDecoratorB(Component* pComponent) : Decorator(pComponent) {
        strcat_s(m_pComponent->Operation(), 255, " --> 装饰了 B ‘饰品‘");
    }

    char* Operation() {
        return m_pComponent->Operation();
    }
};

int _tmain(int argc, _TCHAR* argv[]) {
    Component* test = new ConcreateComponent();
    cout << test->Operation() << endl;

    Component* test_A = new ConcreteDecoratorA(test);
    cout << test_A->Operation() << endl;

    Component* test_A_B = new ConcreteDecoratorB(test_A);
    cout << test_A_B->Operation() << endl;

    system("PAUSE");
    return 0;
}

输出如下:

看下它的优点:

1、装饰者模式可以提供比继承更多的灵活性

2、可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为.

3、通过使用不同的具体装饰类以及这些装饰类的排列组合, 可以创造出很多不同行为的组合. 可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象.

4、具体构件类与具体装饰类可以独立变化, 用户可以根据需要增加新的具体构件类和具体装饰类, 在使用时再对其进行组合, 原有代码无须改变, 符合"开闭原则".

它的缺点也是一样的明显:

1、会产生很多的小对象, 增加了系统的复杂性

2、这种比继承更加灵活机动的特性, 也同时意味着装饰模式比继承更加易于出错, 排错也很困难, 对于多次装饰的对象, 调试时寻找错误可能需要逐级排查, 较为烦琐.

实际开发情景:星巴克以扩张速度快而闻名, 在里面购买咖啡时, 可以要求在其中加入各种调料, 星巴克会根据所加入的调料收取不同的费用, 也就是说不同的咖啡与调料之间有N多不同的组合方式. 每种咖啡和调料都有不同的收费. 如果他们此时正在急于实现一套由计算机管理的自动化记账系统, 在这个时候我们使用继承方式, 则会陷入无以复加的地步, 这里会有N多个类, 出现“类爆炸”现象.

此时就可以实用我们的装饰者模式:

具体代码省略, 参考:http://blog.chinaunix.net/uid-20761674-id-304542.html

模式的简化

如果只有一个ConcreteComponent类, 那么可以考虑去掉Cpmponent类, 让Decorator直接继承ConcreteComponent类, 重写ConcreteComponent的Operation方法.

同样, 如果只有一个ConreteDecortor类, 那么也可以去掉单独的Decorator类, 把Decorator和ConreteDecortor的合并.

 1 #include <iostream>
 2 using namespace std;
 3
 4 class ConcreateComponent {
 5 public:
 6     ConcreateComponent() {
 7         strcpy_s(m_description, 255, "初始状态");
 8     }
 9
10     virtual char* Operation() {
11         return m_description;
12     }
13
14 protected:
15     char m_description[256];
16 };
17
18 class Decorator
19     : public ConcreateComponent {
20 public:
21     Decorator(ConcreateComponent* pComponent) {
22         m_pComponent = pComponent;
23     }
24
25     virtual char* Operation() = 0;
26
27 protected:
28     ConcreateComponent* m_pComponent;
29 };
30
31 class ConcreteDecoratorA
32     : public Decorator {
33 public:
34     ConcreteDecoratorA(ConcreateComponent* pComponent) : Decorator(pComponent) {
35         strcat_s(m_pComponent->Operation(), 255, " --> 装饰了 A ‘饰品‘");
36     }
37
38     char* Operation() {
39         return m_pComponent->Operation();
40     }
41 };
42
43 class ConcreteDecoratorB
44     : public Decorator {
45 public:
46     ConcreteDecoratorB(ConcreateComponent* pComponent) : Decorator(pComponent) {
47         strcat_s(m_pComponent->Operation(), 255, " --> 装饰了 B ‘饰品‘");
48     }
49
50     char* Operation() {
51         return m_pComponent->Operation();
52     }
53 };
54
55 int _tmain(int argc, _TCHAR* argv[]) {
56     ConcreateComponent* test = new ConcreateComponent();
57     cout << test->Operation() << endl;
58
59     ConcreateComponent* test_A = new ConcreteDecoratorA(test);
60     cout << test_A->Operation() << endl;
61
62     ConcreateComponent* test_A_B = new ConcreteDecoratorB(test_A);
63     cout << test_A_B->Operation() << endl;
64
65     system("PAUSE");
66     return 0;
67 }
时间: 2024-10-31 20:34:47

7、装饰者模式的相关文章

装饰者模式

* 通过使用修饰模式,可以在运行时扩充一个类的功能. * 原理是:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数. * 装饰类实现新的功能,而在不需要用到新功能的地方,它可以直接调用原来的类中的方法. * 修饰类必须和原来的类有相同的接口. * 修饰模式是类继承的另外一种选择.类继承在编译时候增加行为,而装饰模式是在运行时增加行为. * 当某个类具有多种可组合叠加的行为或属性时,装饰者模式可以通过动态增加装饰类,以避免排列组合,减少需要类的数量. 共同的

javascript设计模式学习之——装饰者模式

一.装饰者模式定义 装饰者模式可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象.这种为对象动态添加职责的方式就称为装饰者模式.装饰者对象和它所装饰的对象拥有一致的接口,对于用户来说是透明的. 和java等语言不同的是,java的装饰器模式是给对象动态添加职责,javascript中,给对象添加职责的能力是与生俱来的,更侧重于给函数动态添加职责. 二.java中的装饰者模式实现 package com.bobo.shejimoshi.derector; public cl

每天一段笔记-装饰者模式(Decorator pattern)

知识点 类应该对扩展开放,对修改封闭. 案例 (本故事纯属虚构) 某日早上,流年刚把新开发的游戏项目提交给经理 1 public abstract class Role 2 { 3 public virtual string RoleName { get; private set; } 4 public abstract int PhysicalAttack(); 5 } 1 class Samurai : Role 2 { 3 public override string RoleName 4

Java设计模式菜鸟系列(三)装饰者模式建模与实现

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39759199 装饰者(Decorator)模式:动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更具有弹性的替代方案.对于装饰者模式,它其实是一种包装,所以我更愿意称它为一种包装.像咱们以前经常使用的Java里面的IO流就用到了装饰者模式.比如:BufferedReader br = new BufferedReader(new InputStreamReader(new Fi

JS设计模式之装饰者模式

装饰者模式概述 在不改变原对象的基础上,通过对其进行包装拓展(添加属性或者方法)使原有对象可以满足用户更复杂的需求 实际需求 在已有的代码基础上,为每个表单中的input默认输入框上边显示一行提示文案,当用户点击输入框时文案消失 原有代码: var telInput = document.getElementById('tel_input'); var telWarnText = document.getElementById('tel_warn_text'); input.onclick =

Java设计模式の装饰者模式

目录 一.问题引入 二.设计原则 三.用装饰者模式解决问题 四.装饰者模式的特点 五.装饰者模式的定义 六.装饰者模式的实现 七.java.io包内的装饰者模式 一.问题引入 咖啡店的类设计: 一个饮料基类,各种饮料类继承这个基类,并且计算各自的价钱. 饮料中需要加入各种调料,考虑在基类中加入一些布尔值变量代表是否加入各种调料,基类的cost()中的计算各种调料的价钱,子类覆盖cost(),并且在其中调用超类的cost(),加上特定饮料的价钱,计算出子类特定饮料的价钱. 缺点:类数量爆炸.基类加

【设计模式】之装饰器模式

为什么会有装饰模式? 装饰模式是为了解决继承强依赖性和出现大量子类不方便管理问题而出现的.   1. 概述 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活. 原理:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数.装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法.修饰类必须和原来的类有相同的接口. 2. 模式中的角色 2.1 抽象构建(Component):定义一个抽象接口,用以给这些对象动态

Decorator Pattern(装饰器模式)Demo1

一个简单的装饰器模式例子,用来体现直接查询,缓存查询,和过滤敏感词汇 1 import java.sql.Connection; 2 import java.sql.PreparedStatement; 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.util.ArrayList; 6 import java.util.HashMap; 7 import java.util.List; 8 im

装饰器模式

定义:可以动态地添加修改类的功能 解析:一个类提供了一项功能,如果要在修改并添加额外的功能,传统的编程模式,需要写一个子类继承它,并重新实现类的方法.使用装饰器模式,仅需在运行时添加一个装饰器对象即可实现,可以实现最大的灵活性. $canvas1 = new IMooc\Canvas();$canvas1->init();$canvas1->addDecorator(new \IMooc\ColorDrawDecorator('green'));$canvas1->addDecorato

06JavaIO详解_IO流中的设计模式-装饰者模式

Io流里面的过滤流和节点流用的就是装饰者模式.整个的IO体系就是装饰模式. 这个写法就是装饰者模式.对上面的三个已经存在的类(DataOutputStream,BufferedOutputStream,FileOutputStream)进行组合,具有三种功能. 对于DataOutputStream dos=new DataOutStream(new BufferedOutputStream(new FileOutputStream("data.txt"))); 这行代码来说. OutP