- 裝飾者模式
動態地將責任附加到對象上。若要擴展功能,裝飾者提供比繼承更有彈性的替代方案。
- 應用分析——對象的功能擴展
對象的功能擴展,最簡單的方式就是繼承,但是幾種不同的功能有不同的組合時,將會產生許許多多的類,這樣不利於管理。採用裝飾者模式,讓裝飾者與實際對象有相同的接口(繼承同一抽象類),通過組合或委託的方式引用被裝飾對象,然後對被裝飾對象進行動態擴展,可以獲得更大的靈活性。
- 實例分析——星巴克有各種咖啡:HouseBlend、DarkRoast、Decaf、Espresso,客戶購買時,可以要求加入各種調料,如:SteamedMilk、Soy、Mocha等等,星巴克會根據不同的調料進行收費,如果考慮用繼承實現,那麼類數將會爆發性增長,如果考慮使用標記變量,這可能是一個解決方案,但是不利於擴展,用裝飾者模式,可以巧妙解決這一問題。
- 代碼分析
//Component.h
//公共接口
#ifndef COMPONENT_H
#define COMPONENT_H
#include <string>
class Component//被裝飾對象和裝飾者公共的接口
{
protected:
std::string desc;
public:
virtual std::string getdesc()
{
return desc;
}
virtual double cost()=0;
virtual ~Component(){}
};
#endif
//ConcreteComponent.h
//被裝飾者
#ifndef CONCRETECOMPONENT_H
#define CONCRETECOMPONENT_H
#include "Component.h"
//兩種不同的咖啡
class HouseBlend:public Component
{
public:
HouseBlend()
{
desc="HouseBlend";
}
double cost()
{
return 10.0;
}
};
class DarkRoast:public Component
{
public:
DarkRoast()
{
desc="DarkRoast";
}
double cost()
{
return 20.0;
}
};
#endif
//Decorator.h
//裝飾者
#ifndef DECORATOR_H
#define DECORATOR_H
#include "Component.h"
//兩種調料
class Milk:public Component
{
private:
Component *ptrCom;//被裝飾者
public:
Milk(Component *ptrCom)
{
this->ptrCom=ptrCom;
}
std::string getdesc()
{
return ptrCom->getdesc()+", Milk";
}
double cost()
{
return ptrCom->cost()+5.0;
}
};
class Mocha:public Component
{
private:
Component *ptrCom;
public:
Mocha(Component *ptrCom)
{
this->ptrCom=ptrCom;
}
std::string getdesc()
{
return ptrCom->getdesc()+", Mocha";
}
double cost()
{
return ptrCom->cost()+15.0;
}
};
#endif
//main.cpp
//測試程序
#include <iostream>
#include "Decorator.h"
#include "ConcreteComponent.h"
#define print(x) std::cout<<x->getdesc()<<": $"<<x->cost()<<std::endl
int main()
{
Component *HB=new HouseBlend();
print(HB);
Component *DR=new DarkRoast();
print(DR);
Component *HB_Milk=new Milk(HB);
print(HB_Milk);
Component *HB_Milk_M=new Mocha(HB_Milk);
print(HB_Milk_M);
Component *DR_M=new Mocha(DR);
print(DR_M);
delete HB;
delete HB_Milk;
delete HB_Milk_M;
delete DR;
delete DR_M;
return 0;
}