定义:
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。
示例一:装饰模式(通用版)
1. 类图17-5
2. 类图说明
- Component 抽象构件。这是一个接口或抽象类,就是定义我们最核心的对象,也就是最原始的对象,如下面的成绩单。(注:在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当 Component 抽象构建)。
- ConcreteComponent 具体构件。这是最核心、最原始的接口或抽象类的实现,需要装饰的就是它。
- Decorator 装饰角色。一般是一个抽象类,实现接口或者抽象方法,它里面可不一定有抽象方法,在他的属性里必然有一个 private 变量指向 Component 抽象构件。
- 具体装饰角色。ConcreteDecoratorA 和 ConcreteDecoratorB 是两个具体的装饰类,需要把最核心、最原始的东西装饰成其他东西,下面的例子就是把一个比较平庸的成绩单装饰成家长认可的成绩单。
3. 代码清单17-4
////////////////////// ********** 4. 装饰模式(通用版),代码清单17-4:***************//
class Component
{
public:
virtual void operate() = 0;
};
class ConcreteComponent :public Component
{
public:
virtual void operate() //override
{
qDebug() << "do something";
}
};
class Decorator :public Component
{
public:
Decorator(Component* component)
{
this->m_component = component;
}
virtual void operate() //override
{
this->m_component->operate();
}
private:
Component *m_component;
};
class ConcreteDecorator1:public Decorator
{
public:
ConcreteDecorator1(Component* component):Decorator(component){}
virtual void operate() //override
{
this->method1();
Decorator::operate();
}
private:
void method1(){qDebug() << "method1"; }
};
class ConcreteDecorator2:public Decorator
{
public:
ConcreteDecorator2(Component* component):Decorator(component){}
virtual void operate() //override
{
this->method2();
Decorator::operate();
}
private:
void method2(){qDebug() << "method2"; }
};
int main()
{
Component *component = new ConcreteComponent();
Component *component2 = new ConcreteDecorator1(component);
Component *component3 = new ConcreteDecorator2(component);
component2->operate();
component3->operate();
return 0;
}
示例二:成绩单(初设计)
1. 类图17-1
2. 类图说明
一个成绩单的抽象类,一个四年级的成绩单实现类
3. 家长查看成绩单类图17-2
4. 代码清单
//////////////////// ********** 1. 成绩单,代码清单17-1:***************//
class SchoolReport
{
public:
virtual void report() = 0;
virtual void sign() = 0;
};
class FouthGradeSchoolReport: public SchoolReport
{
public:
virtual void report()
{
qDebug() << "Chinese : 62";
qDebug() << "Math : 65";
qDebug() << "English : 63";
}
virtual void sign()
{
qDebug() << "sign!";
}
};
int main()
{
SchoolReport *sr = new FouthGradeSchoolReport();
sr->report();
return 0;
}
5. 问题:成绩太低,不给签
6. 改善:把成绩单做一个封装,封装分两步来实现
- 汇报最高成绩
- 汇报排名情况
示例三:成绩单(改善版:添加装饰)
1. 类图17-3
2. 类图说明
通过直接增加了一个子类,重写 report 方法
3. 代码清单17-2
////////////////////// ********** 2. 成绩单(修饰后),代码清单17-2:***************//
class SchoolReport
{
public:
virtual void report() = 0;
virtual void sign() = 0;
};
class FouthGradeSchoolReport: public SchoolReport
{
public:
virtual void report()
{
qDebug() << "Chinese : 62";
qDebug() << "Math : 65";
qDebug() << "English : 63";
}
virtual void sign()
{
qDebug() << "sign!";
}
};
class SugarFGSR:public FouthGradeSchoolReport
{
public:
virtual void report()
{
this->reportHighScore();
FouthGradeSchoolReport::report();
this->reportSort();
}
private:
void reportHighScore(){qDebug() << "Math : 65";}
void reportSort(){qDebug() << "number: 38";}
};
int main()
{
SchoolReport *sr = new SugarFGSR();
sr->report();
sr->sign();
return 0;
}
4. 问题:一旦需要装饰的条件非常多,通过继承解决就很麻烦了
示例四:成绩单(再改善:较多修饰情况)
1. 类图17-4
2. 类图说明:
增加一个抽象类和两个实现类,其中 Decorator 的作用是封装 SchoolReport 类,这里类似一个代理,装饰类的作用也就是一个特殊的代理类,真实的执行者还是被代理的角色 FouthGradeSchoolReport
3. 代码清单17-3
////////////////////// ********** 3. 成绩单(较多修饰情况),代码清单17-3:***************//
class SchoolReport
{
public:
virtual void report() = 0;
virtual void sign() = 0;
};
class FouthGradeSchoolReport: public SchoolReport
{
public:
virtual void report()
{
qDebug() << "Chinese : 62";
qDebug() << "Math : 65";
qDebug() << "English : 63";
}
virtual void sign()
{
qDebug() << "sign!";
}
};
class Decorator:public SchoolReport
{
public:
Decorator(SchoolReport *report)
{
this->m_report = report;
}
virtual void report()
{
this->m_report->report();
}
virtual void sign()
{
this->m_report->sign();
}
private:
SchoolReport *m_report;
};
class HighScoreDecorator:public Decorator
{
public:
HighScoreDecorator(SchoolReport *report):Decorator(report){}
virtual void report()
{
this->reportHighScore();
Decorator::report();
}
private:
void reportHighScore(){qDebug() << "Math : 65";}
};
class SortDecorator:public Decorator
{
public:
SortDecorator(SchoolReport *report):Decorator(report){}
virtual void report()
{
Decorator::report();
this->reportSort();
}
private:
void reportSort(){qDebug() << "number: 38";}
};
int main()
{
SchoolReport *sr1 = new FouthGradeSchoolReport(); //原装成绩单
SchoolReport *sr2 = new HighScoreDecorator(sr1); //加了最高分说明的成绩单
SchoolReport *sr3 = new SortDecorator(sr1); //加了成绩排名的说明
sr3->report();
sr3->sign();
return 0;
}
五、装饰模式的应用
1. 优点
- 装饰类和被装饰类可以独立发展,而不会相互耦合。也就是说,Component 类无需知道 Decorator 类,Decorator 类是从外部来扩展 Component 类的功能,而 Decorator 也不用知道具体的构件。
- 装饰模式是继承关系的一个替代方案。 我们看装饰类 Decorator ,不管装饰多少层,返回的对象还是 Component ,实现的还是is-a关系。
- 装饰模式可以动态地扩展一个实现类的功能。
2. 缺点
多层的装饰是比较复杂的,需要尽量减少装饰类的数量,以便降低系统的复杂度
3. 使用场景:
需要扩展一个类的功能,或给一个类增加附加功能。
需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
需要为一批的兄弟类进行改装或加装功能,首选装饰模式。
六、最佳实践
- 装饰模式是对继承有力的补充。要知道继承不是万能的,继承可以解决实际问题,但是在项目中要考虑诸如易维护、易扩展、易服用等,而且在一些情况下,如果用继承就会增加许多子类,而且灵活性非常差,当然也不容易维护,也即,装饰模式可以替代继承,解决我们类膨胀的问题。
- 继承是静态的给类增加功能,而装饰模式则是动态地增加功能。
- 扩展性很好。在一个项目中,会有非常多的因素考虑不到,特别是业务的变更,不时地冒出一个需求,尤其是提出一个令项目大量延迟的需求时,通过装饰模式重新封装一个类,而不时通过继承来完成。
- 三个继承关系Father、Son、GrandSon 三个类,要在Son类上增加功能时如果修改 Son类中的方法是工作量很大的方式。可以通过建立 SonDecorator类来修饰 Son,相当于创建了一个新的类,这个对原有程序没有变更,通过扩展很好地完成了这次变更。
参考文献《秦小波. 设计模式之禅》(第2版) (华章原创精品) 机械工业出版社