装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
——— 百度百科
对于装饰这个概念来说,我们要明白两个东西:
- 主体(被装饰者)
- 饰品(装饰)
比如:定做的奶油蛋糕,蛋糕是主体,而蛋糕的外包装、丝带、花朵都是饰品。
在某些情况下我们可能会"过度地使用继承来扩展对象的功能",由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
上面蛋糕的例子中,蛋糕和外包装和系的丝带以及花朵装饰,他们之间的关系都是通过组合而成的,这些东西虽然最终是蛋糕店生产卖给消费者,但是蛋糕店中每种材料均可以从别的厂家采购:可以奶油厂家、塑料丝厂家、花朵厂家、(可能还有蜡烛厂家)。
想一想,为什么不按照下面的想法做?
想法:买家需求不同,有的买家不要假花(因为不能吃),有的买家不要蜡烛,因为住所禁止燃火(如学校宿舍)…
做法:量产套餐 (每个套餐其实就是一个类)
假设可供选择的有这样几项
名称 选择 类型 假花(Flower) 红色、蓝色、白色、不选 RedFlower/BlueFlower/WhiteFlower/NoneFlower 蜡烛(Candle) 红色、蓝色、白色、不选 RedCandle/BlueCandle/WhiteCandle/NoneCandle 计算套餐组合情况 (4 x 4 = 16个套餐类) 套餐名称 详细情况 类型 ---- ---- ---- 套餐A 红色假花 + 红色蜡烛 RedFlowerRedCandle 套餐B 红色假花 + 蓝色蜡烛 RedFlowerBlueCandle 套餐C 红色假花 + 白色蜡烛 RedFlowerWhiteCandle 套餐D 红色假花 + 无蜡烛 RedFlowerNoneCandle … … … 套餐P 不选假花 + 不选蜡烛 NoneFlowerNoneCandle
在设计模式中,为了解决这种子类的膨胀,通常利用"组合"来代替继承。这也是"单一职责"模式的要求,在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复码,这时候的关键是划清责任。
这里用制作咖啡为例,(———案例来自尚硅谷设计模式)
1. 咖啡底料:
意式浓缩咖啡(espresso)、卡布奇诺(cappuccino)、黑咖啡(blackcoffee)
2. 咖啡辅料:
糖(sugar)、巧克力(chocolate)、牛奶(milk)
按照套餐思路:
计算一下,发现,应该有3 x 3 = 9种组合,啊不对!因为加了糖也可以加巧克力,而且份数也可以不只一份,这个不太好算,把每个套餐写成一个类,每个套餐都有自己独立的计价。
但是,当咖啡底料或者辅料增加时,就需要增加大量的类,比如有了上面9种套餐之后,突然辅料新出来了蜂蜜,则需要与之前所有已经存在的饮品组合一下,脑袋已经短路了!
装饰模式思路:
这个时候不用慌,想象过度包装是什么样子的。一个主要的核心物品,套一层外壳,再套一层,再套一层…这里,核心物品不就是上面所说的咖啡底料吗?外壳不就是这些咖啡辅料吗?
一份意式浓缩咖啡 + 一份糖 + 二份巧克力 + 三份牛奶
就变成:
一份意式浓缩咖啡(主体) => 纯咖啡
纯咖啡 + 放在一份糖里面混合 => 混合咖啡
混合咖啡 + 放在一份巧克力里面混合 => 混合咖啡
混合咖啡 + 放在一份巧克力里面混合 => 混合咖啡
混合咖啡 + 放在一份牛奶里面混合 => 混合咖啡
混合咖啡 + 放在一份牛奶里面混合 => 混合咖啡
混合咖啡 + 放在一份牛奶里面混合 => 混合咖啡
1. new 出女孩子
Girl* girl = new Girl();
2. 把女孩子"放入"装饰1中进行包装:
Decorator1* dec1 = new Decorator1(girl),传入girl,这个时候装饰1变量dec1就表现出女孩子属性,具体则是原来的女孩子和装饰1的整体。
3. 此时再给她增加一个装饰名为装饰2:
Decorator2* dec2 = new Decorator1(dec1),传入dec1就代指带有装饰1配饰的girl,这个时候装饰2变量dec2也表现出女孩子属性,具体则是原来的女孩子和装饰1、装饰2的整体。
注:与链式编程类似,当一个函数返回的的本类对象时就会出现:
object.func().func().func()…
常见的有:arg(),这个是加参数的,因为函数会返回自身的引用,object.func()执行完毕,返回一个*this,那就是object,再调用func()方法,完全没问题!
我们又发现它的另一个特点——装饰在主体上的搭配十分灵活,比如你想戴耳钉还是吊坠,围什么样式的围巾,采用什么风格的妆容、什么形式的衣服搭配…你自己选,你甚至可以穿两条裤子,三件上衣,当然也可以穿皇帝的新装…
- 要有主体,如果感觉都像是装饰,可以选一个自己好理解的事物作为主体。
- 在各种可以与主体搭配的事物之上抽象出"装饰品"这个概念,使得这些次要(和主体相对)辅助事物继承自该装饰品,这样有利于这些事物获得装饰品中的一个"核心事物"。
- “装饰品"最最最大的特点:继承自主体(一般是主体的基类,下面会解释说明),类内存在一个私有成员——2.中提到的"核心事物”,也就是所继承父类的指针
装饰品示例
class Decorator: public Coffe
{
protected:
Coffe* coffe;
public:
Decorator(Coffe* cof) : coffe(cof) {}
};
其中,Coffee是主体即咖啡底料(espresso、cappuccino、blackcoffee)的基类,可以理解为抽象主体,由此可见,装饰者Decorator既继承自抽象主体,又具有成员抽象主体指针。
为什么装饰者要这么做?
因为上面说了:装饰的特点——装饰包装主体并且包装后的混合整体表现的性质与原来主体的相同,拆开来看:
Coffe* coffe;
需求:满足不同客户对咖啡口味的需求
咖啡基类:coffe.h
#pragma once
#include
using namespace std;
class Coffe
{
public:
Coffe() {}
virtual string desc() = 0;
virtual float cost() = 0;
};
咖啡底料(主体):
#pragma once
#include "coffe.h"
using namespace std;
class Espresso : public Coffe
{
public:
Espresso() {}
virtual string desc() { // 咖啡描述
return "意大利式";
}
virtual float cost() { // 一杯意式浓咖啡的价格
return 60.5f;
}
};
#pragma once
#include "coffe.h"
using namespace std;
class Cappuccino : public Coffe
{
public:
Cappuccino() {}
virtual string desc() {
return "卡布奇诺";
}
virtual float cost() {
return 30.0f;
}
};
#pragma once
#include "coffe.h"
using namespace std;
class BlackCoffee : public Coffe
{
public:
BlackCoffee() {}
virtual string desc() {
return "黑咖啡";
}
virtual float cost() {
return 25.0f;
}
};
装饰品:decorator.h
#pragma once
#include
#include "coffe.h"
class Decorator: public Coffe
{
protected:
Coffe* coffe;
public:
Decorator(Coffe* cof) : coffe(cof) {}
};
咖啡辅料(装饰)
#pragma once
#include "decorator.h"
#include
class Sugar : public Decorator
{
public:
Sugar(Coffe* cof) : Decorator(cof) {}
virtual string desc() {
return coffe->desc() + "一勺糖";
}
virtual float cost() {
return coffe->cost() + 1.0f; // 一勺糖1¥
}
};
#pragma once
#include "decorator.h"
class Chocolate : public Decorator
{
public:
Chocolate(Coffe* cof) : Decorator(cof) {}
virtual string desc() {
return coffe->desc() + "一块巧克力";
}
virtual float cost() {
return coffe->cost() + 3.0f;// 一块巧克力3¥
}
};
#pragma once
#include "decorator.h"
#include "coffe.h"
class Milk : public Decorator
{
public:
Milk(Coffe* cof) : Decorator(cof) {}
virtual string desc() {
return coffe->desc() + "一勺牛奶";
}
virtual float cost() {
return coffe->cost() + 2.0f;// 一勺牛奶2¥
}
};
测试:
#include
#include "blackcoffee.h"
#include "cappuccino.h"
#include "espresso.h"
#include "chocolate.h"
#include "milk.h"
#include "sugar.h"
using namespace std;
int main() {
// 点一杯黑咖啡
BlackCoffee* bc = new BlackCoffee();
// 计算一杯黑咖啡的价格
cout << bc->desc() << " " << bc->cost() << endl;
// 加一勺糖
Sugar* sg = new Sugar(bc);
// 计算一杯黑咖啡加一勺糖的价格
cout << sg->desc() << " " << sg->cost() << endl;
// 加一勺牛奶
Milk* mk = new Milk(sg);
// 计算一杯黑咖啡加一勺糖、加一勺牛奶的价格
cout << mk->desc() << " " << mk->cost() << endl;
// 加一块巧克力
Chocolate* ct = new Chocolate(mk);
// 计算一杯黑咖啡加一勺糖、加一勺牛奶、加一块巧克力的价格
cout << ct->desc() << " " << ct->cost() << endl;
return 0;
}
输出结果:
黑咖啡 25
黑咖啡一勺糖 26
黑咖啡一勺糖一勺牛奶 28
黑咖啡一勺糖一勺牛奶一块巧克力 31
需求:实现分别对文件流、内存流、网络流的加密、缓存以及加密缓存的操作
decorator1.cpp
//业务操作
class Stream{
public:
virtual char Read(int number)=0;
virtual void Seek(int position)=0;
virtual void Write(char data)=0;
virtual ~Stream(){}
};
//主体类
class FileStream: public Stream{
public:
virtual char Read(int number){
//读文件流
}
virtual void Seek(int position){
//定位文件流
}
virtual void Write(char data){
//写文件流
}
};
class NetworkStream :public Stream{
public:
virtual char Read(int number){
//读网络流
}
virtual void Seek(int position){
//定位网络流
}
virtual void Write(char data){
//写网络流
}
};
class MemoryStream :public Stream{
public:
virtual char Read(int number){
//读内存流
}
virtual void Seek(int position){
//定位内存流
}
virtual void Write(char data){
//写内存流
}
};
//扩展操作
class CryptoFileStream :public FileStream{
public:
virtual char Read(int number){
//额外的加密操作...
FileStream::Read(number);//读文件流
}
virtual void Seek(int position){
//额外的加密操作...
FileStream::Seek(position);//定位文件流
//额外的加密操作...
}
virtual void Write(byte data){
//额外的加密操作...
FileStream::Write(data);//写文件流
//额外的加密操作...
}
};
class CryptoNetworkStream : public NetworkStream{
public:
virtual char Read(int number){
//额外的加密操作...
NetworkStream::Read(number);//读网络流
}
virtual void Seek(int position){
//额外的加密操作...
NetworkStream::Seek(position);//定位网络流
//额外的加密操作...
}
virtual void Write(byte data){
//额外的加密操作...
NetworkStream::Write(data);//写网络流
//额外的加密操作...
}
};
class CryptoMemoryStream : public MemoryStream{
public:
virtual char Read(int number){
//额外的加密操作...
MemoryStream::Read(number);//读内存流
}
virtual void Seek(int position){
//额外的加密操作...
MemoryStream::Seek(position);//定位内存流
//额外的加密操作...
}
virtual void Write(byte data){
//额外的加密操作...
MemoryStream::Write(data);//写内存流
//额外的加密操作...
}
};
class BufferedFileStream : public FileStream{
//...
};
class BufferedNetworkStream : public NetworkStream{
//...
};
class BufferedMemoryStream : public MemoryStream{
//...
}
class CryptoBufferedFileStream :public FileStream{
public:
virtual char Read(int number){
//额外的加密操作...
//额外的缓冲操作...
FileStream::Read(number);//读文件流
}
virtual void Seek(int position){
//额外的加密操作...
//额外的缓冲操作...
FileStream::Seek(position);//定位文件流
//额外的加密操作...
//额外的缓冲操作...
}
virtual void Write(byte data){
//额外的加密操作...
//额外的缓冲操作...
FileStream::Write(data);//写文件流
//额外的加密操作...
//额外的缓冲操作...
}
};
void Process(){
//编译时装配
CryptoFileStream *fs1 = new CryptoFileStream();
BufferedFileStream *fs2 = new BufferedFileStream();
CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();
}
decorator2.cpp
//业务操作
class Stream{
public:
virtual char Read(int number)=0;
virtual void Seek(int position)=0;
virtual void Write(char data)=0;
virtual ~Stream(){}
};
//主体类
class FileStream: public Stream{
public:
virtual char Read(int number){
//读文件流
}
virtual void Seek(int position){
//定位文件流
}
virtual void Write(char data){
//写文件流
}
};
class NetworkStream :public Stream{
public:
virtual char Read(int number){
//读网络流
}
virtual void Seek(int position){
//定位网络流
}
virtual void Write(char data){
//写网络流
}
};
class MemoryStream :public Stream{
public:
virtual char Read(int number){
//读内存流
}
virtual void Seek(int position){
//定位内存流
}
virtual void Write(char data){
//写内存流
}
};
//扩展操作
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);//写文件流
//额外的加密操作...
}
};
class BufferedStream : public Stream{
Stream* stream;//...
public:
BufferedStream(Stream* stm):stream(stm){
}
//...
};
void Process(){
//运行时装配
FileStream* s1=new FileStream();
CryptoStream* s2=new CryptoStream(s1);
BufferedStream* s3=new BufferedStream(s1);
BufferedStream* s4=new BufferedStream(s2);
}
decorator3.cpp
//业务操作
class Stream{
public:
virtual char Read(int number)=0;
virtual void Seek(int position)=0;
virtual void Write(char data)=0;
virtual ~Stream(){}
};
//主体类
class FileStream: public Stream{
public:
virtual char Read(int number){
//读文件流
}
virtual void Seek(int position){
//定位文件流
}
virtual void Write(char data){
//写文件流
}
};
class NetworkStream :public Stream{
public:
virtual char Read(int number){
//读网络流
}
virtual void Seek(int position){
//定位网络流
}
virtual void Write(char data){
//写网络流
}
};
class MemoryStream :public Stream{
public:
virtual char Read(int number){
//读内存流
}
virtual void Seek(int position){
//定位内存流
}
virtual void Write(char data){
//写内存流
}
};
//扩展操作
class DecoratorStream: public Stream{
protected:
Stream* stream;//...
DecoratorStream(Stream * stm):stream(stm){
}
};
class CryptoStream: public DecoratorStream {
public:
CryptoStream(Stream* stm):DecoratorStream(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);//写文件流
//额外的加密操作...
}
};
class BufferedStream : public DecoratorStream{
Stream* stream;//...
public:
BufferedStream(Stream* stm):DecoratorStream(stm){
}
//...
};
void Process(){
//运行时装配
FileStream* s1=new FileStream();
CryptoStream* s2=new CryptoStream(s1);
BufferedStream* s3=new BufferedStream(s1);
BufferedStream* s4=new BufferedStream(s2);
}
本文主要采用了尚硅谷韩老师的例子,链接如下:尚硅谷-设计模式-装饰者模式,老师讲得很清楚,推荐!
THE END…