我们的游戏中一般都会有UI(用户接口)界面,比如背包中的格子:
或者一些文本UI:
比如现在想要绘制这样一个界面,先后为一个普通的列表控件增加边框、垂直滚动条、水平滚动条:
我们可以采用继承的设计思路:
但是后续如果我们想要增加其他效果,就又需要继承新的子类。
我们修改一下设计思路,将继承的方式改为组装的方式来解决,从而防止类泛滥:
上面这种通过装饰方式,将一个类的功能不断增强的思想(动态地增加新功能),就是装饰器模式的思想。
根据“组合复用原则”,如果两个类使用继承进行设计,则父类代码的修改可能影响子类的行为,而且可能父类中的很多方法子类是用不上的,这显然是一种浪费,如果使用组合进行设计,则可以大大降低两个类之间的依赖关系,也不会存在因继承关系导致的浪费行为。
所以如果继承和组合都能够达到设计目的,则优先考虑使用组合。
我们将不同的装饰器类分别实现,并且他们都继承自抽象的装饰器类,并且可以调用传入的抽象类指针来执行传入的装饰器功能。
#include
using namespace std;
namespace hjl_project1
{
//抽象的列表类
class Control
{
public:
//将自身绘制到屏幕上
virtual void draw() = 0;
virtual ~Control() {}
};
//列表本体
class ListCtrl : public Control
{
public:
virtual void draw()
{
//具体的绘制工作可以用openGL等工具,具体代码省略
cout << "绘制了普通的列表" << endl;
}
};
//抽象的装饰器类
class Decorator : public Control
{
public:
Decorator(Control *tmpctrl)
: m_control(tmpctrl)
{
}
virtual void draw()
{
m_control->draw();
}
private:
//需要被装饰的列表
Control *m_control;
};
//-----------------------------------------
//具体的边框装饰器类
class BorderDec : public Decorator
{
public:
BorderDec(Control *tmpctrl)
: Decorator(tmpctrl)
{
}
virtual void draw()
{
//调用父类的draw,绘制列表
Decorator::draw();
drawBorder();
}
private:
void drawBorder()
{
cout << "绘制边框" << endl;
}
};
//具体的垂直滚动条装饰器类
class VerScrollBarDec : public Decorator
{
public:
VerScrollBarDec(Control *tmpctrl)
: Decorator(tmpctrl)
{
}
virtual void draw()
{
//调用父类的draw,绘制列表
Decorator::draw();
drawCerScrollBar();
}
private:
void drawCerScrollBar()
{
cout << "绘制垂直滚动条" << endl;
}
};
//具体的水平滚动条
class HorScrollBarDec : public Decorator
{
public:
HorScrollBarDec(Control *tmpctrl)
: Decorator(tmpctrl)
{
}
virtual void draw()
{
//调用父类的draw,绘制列表
Decorator::draw();
drawHorScrollBar();
}
private:
void drawHorScrollBar()
{
cout << "绘制水平滚动条" << endl;
}
};
}
int main()
{
using namespace hjl_project1;
//创建一个又带边框,又带垂直滚动条的列表
// 1.先创建一个列表本体
Control *plistctrl = new ListCtrl();
// 2.借助本体和装饰器,实现又带边框又带垂直滚动条
Decorator *plistctrl_b = new BorderDec(plistctrl);
Decorator *plistctrl_b_v = new VerScrollBarDec(plistctrl_b);
plistctrl_b_v->draw();
}
可以看到上面的Decorator和Control两个类既是继承关系又是组合关系。
一杯单纯的水果饮料售价10元;如果向饮料中增加砂糖,则额外要多加1元;如果加牛奶,额外多加2元;增加珍珠,多加2元。
如果顾客又加珍珠又加砂糖,则需要13元。
使用装饰器模式来实现这个情景,就比较合适。
#include
using namespace std;
namespace hjl_project2
{
//抽象饮料类
class Beverage
{
public:
virtual int getprice() = 0;
virtual ~Beverage() {}
};
//水果饮料类
class FruitBeverage : public Beverage
{
public:
int getprice()
{
return 10;
}
};
//抽象的装饰器类
class Decorator : public Beverage
{
public:
Decorator(Beverage *tmpbvg)
: m_pbvg(tmpbvg)
{
}
virtual int getprice()
{
return m_pbvg->getprice();
}
private:
Beverage *m_pbvg;
};
//具体的“砂糖”装饰器类
class SugarDec : public Decorator
{
public:
SugarDec(Beverage *tmpbvg)
: Decorator(tmpbvg)
{
}
virtual int getprice()
{
return Decorator::getprice() + 1;
}
};
//具体的“牛奶”装饰器类
class MilkDec : public Decorator
{
public:
MilkDec(Beverage *tmpbvg)
: Decorator(tmpbvg)
{
}
virtual int getprice()
{
return Decorator::getprice() + 2;
}
};
//具体的“珍珠”装饰器类
class BubbleDec : public Decorator
{
public:
BubbleDec(Beverage *tmpbvg)
: Decorator(tmpbvg)
{
}
virtual int getprice()
{
return Decorator::getprice() + 2;
}
};
}
int main()
{
using namespace hjl_project2;
Beverage *pfruit = new FruitBeverage();
//增加珍珠
Decorator *pfruit_addbubble = new BubbleDec(pfruit);
//增加砂糖
Decorator *pfruit_addbubble_addsugar = new SugarDec(pfruit_addbubble);
cout << "增加了珍珠和砂糖的水果饮料的价格为:" << pfruit_addbubble_addsugar->getprice() << endl;
}
如果后续想增加新的饮料类型,比如增加“茶饮料”为20元,则只需要继承抽象类即可:
动态地给一个对象添加一些额外的职责,就增加功能来说,该模式相比生成子类更加灵活(减少了子类泛滥)。符合“开闭原则”。
装饰器模式一般包含四个角色:
装饰器对象和被装饰对象有相同的父类(Control),这样做的好处是装饰器对象就能够取代被装饰的对象,并可以用一个或多个装饰器包装一个对象。
但是这个模式有一个不足之处,那就是会产生许多中间的小对象,占用了资源并且不容易管理。