• 06 装饰(Decorator)模式


    一:问题的提出

    游戏主角包裹的UI界面
    游戏主角包裹的UI界面
    游戏中出现的公告信息的UI界面
    游戏中出现的公告信息的UI界面

    UI(用户接口)界面。
    a)ListCtrl类代表普通列表控件,提供draw方法。
    b)BorderListCtrl类,继承自ListCtrl,增加了边框的列表控件,提供draw方法。
    c)VerScBorderListCtrl类继承自BorderListCtrl,表示增加了边框又增加了垂直滚动条的列表控件,提供draw方法。
    d)HorScVerScBorderListCtrl类,继承自VerScBorderListCtrl,表示增加了边框,垂直、水平滚动条的列表控件,提供draw方法。


    先后为一个普通的列表控件增加边框、垂直滚动条、水平滚动条后的效果

    继承改为组装方式来解决,防止类泛滥
    a)ListCtrl类代表普通列表控件,提供draw方法。
    b)增加边框->带边框的列表控件。
    c)增加垂直滚动条->带纯质滚动条的列表控件,再给这个带垂直滚动条的列表控件增加一个水平滚动条->既带垂直滚动条又带水平滚动条的列表控件。

    为列表控件增加不同的装饰可以得到不同的列表控件
    为列表控件增加不同的装饰可以得到不同的列表控件

    这种通过装饰方式将一个类的功能不断增强的思想(动态的增加新功能),就是装饰模式的核心设计思想。
    public继承:is -a 关系,组合关系和委托关系。

    二:引入装饰(Decorator)模式

    组合复用原则(Composite Reuse Principle,CRP),也称为合成复用原则/聚合复用原则。
    若两个使用继承进行设计,则父类代码的修改可能影响子类的行为,而且,可能父类中的很多方法子类是用不上的,这显然是一种浪费,
    若使用组合进行设计,则可以大大降低两个类之间的依赖关系,也不会存在因继承关系导致的浪费行为。所以,如果继承和组合都能达到设计目的,优先考虑使用组合(组合优于继承)。

    #ifdef _DEBUG  //只在Debug(调试)模式下
    #ifndef DEBUG_NEW
    #define DEBUG_NEW new(_NORMAL_BLOCK,__FILE__,__LINE__)  //重新定义new运算符
    #define new DEBUG_NEW
    #endif
    #endif
    
    //#include <boost/type_index.hpp>
    using namespace std;
    //#pragma warning(disable : 4996) 
    
    namespace nmsp1
    {
    	//抽象的控件类
    	class Control
    	{
    	public:
    		virtual void draw() = 0;  //draw方法,用于将自身绘制到屏幕上。
    	public:
    		virtual ~Control() {}  //做父类时析构函数应该为虚函数
    	};
    
    	//列表控件类
    	class ListCtrl :public Control
    	{
    	public:
    		virtual void draw()
    		{
    			cout << "绘制普通的列表控件!" << endl;  //具体可以用DirectX或OpenGL来绘制
    		}
    	};
    
    	//抽象的装饰器类
    	class Decorator :public Control
    	{
    	public:
    		Decorator(Control* tmpctrl) :m_control(tmpctrl) {}  //构造函数
    		virtual void draw()
    		{
    			m_control->draw();  //虚函数,调用的是哪个draw,取决于m_control指向的对象
    		}
    	private:
    		Control* m_control;  //需要被装饰的其他控件,这里用的是Control*;
    	};
    
    	//具体的“边框”装饰器类
    	class BorderDec :public Decorator
    	{
    	public:
    		BorderDec(Control* tmpctrl) :Decorator(tmpctrl) {}  //构造函数
    		virtual void draw()
    		{
    			Decorator::draw();  //调用父类的draw方法以保持以往已经绘制出的内容
    			drawBorder();  //也要绘制自己的内容
    		}
    	private:
    		void drawBorder()
    		{
    			cout << "绘制边框!" << endl;
    		}
    	};
    
    	//具体的“垂直滚动条”装饰器类
    	class VerScrollBarDec :public Decorator
    	{
    	public:
    		VerScrollBarDec(Control* tmpctrl) :Decorator(tmpctrl) {}  //构造函数
    		virtual void draw()
    		{
    			Decorator::draw();  //调用父类的draw方法以保持以往已经绘制出的内容
    			drawVerScrollBar();  //也要绘制自己的内容
    		}
    	private:
    		void drawVerScrollBar()
    		{
    			cout << "绘制垂直滚动条!" << endl;
    		}
    	};
    
    	//具体的“水平滚动条”装饰器类
    	class HorScrollBarDec :public Decorator
    	{
    	public:
    		HorScrollBarDec(Control* tmpctrl) :Decorator(tmpctrl) {}  //构造函数
    		virtual void draw()
    		{
    			Decorator::draw();  //调用父类的draw方法以保持以往已经绘制出的内容
    			drawHorScrollBar();  //也要绘制自己的内容
    		}
    	private:
    		void drawHorScrollBar()
    		{
    			cout << "绘制水平滚动条!" << endl;
    		}
    	};
    }
    
    int main()
    {
    	//(1)创建一个又带边框,又带垂直滚动条的列表控件
    	//首先绘制普通的列表控件
    	nmsp1::Control* plistctrl1 = new nmsp1::ListCtrl();  //本体
    
    	//接着“借助普通的列表控件”,可以通过边框装饰器绘制出一个“带边框的列表控件”
    	nmsp1::Decorator* plistctrl_b = new nmsp1::BorderDec(plistctrl1);  //nmsp1::Decorator*用成nmsp1::Control*
    
    	//接着“借助带边框的列表控件”,就可以通过垂直滚动条装饰器绘制出一个“带垂直滚动条又带边框的列表控件”
    	nmsp1::Decorator* plistctrl_b_v = new nmsp1::VerScrollBarDec(plistctrl_b);
    	plistctrl_b_v->draw();  //这里完成最终绘制
    
    	cout << "--------------------" << endl;
    
    	//(2)创建一个只带水平滚动条的列表控件
    	//首先绘制普通的列表控件
    	nmsp1::Control* plistctrl2 = new nmsp1::ListCtrl();  //本体
    
    	//接着“借助普通的列表控件”,可以通过水平滚动条装饰器绘制出一个“带水平滚动条的列表控件”
    	nmsp1::Decorator* plistctrl2_h = new nmsp1::HorScrollBarDec(plistctrl2);
    	plistctrl2_h->draw();
    
    	//(3)释放资源
    	delete plistctrl_b_v;
    	delete plistctrl_b;
    	delete plistctrl1;
    
    	delete plistctrl2_h;
    	delete plistctrl2;
    
    	std::cout << "主线程执行完毕\n";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130

    装饰模式UML图
    装饰模式UML图

    “装饰”设计模式的定义(实现意图):动态的给一个对象添加一些额外的职责。就增加功能来说,该模式相比生成子类更加灵活。
    装饰模式包含的四种角色:
    a:Control(抽象构件):draw,让调用者以一致的方式处理未被修饰的对象以及经过修饰之后的对象,实现客户端的透明操作。
    b:ListCtrl(具体构件):实现抽象构件定义的接口,此后,装饰器就可以给该构件增加额外的方法(职责)。
    c:Decorator(抽象装饰器类)。
    d:BorderDec、VerScrollBarDec、HorScrollBarDesc(具体装饰器类):增加了一些新方法,然后通过对draw接口的扩展,达到最终的修饰目的。

    三:另一个装饰模式的范例

    计算水果饮料最终价格
    a)一杯单纯的水果饮料,售价为10元。
    b)如果向饮料中增加砂糖,则额外要加多1元。
    c)如果向饮料中增加牛奶,则额外要加多2元。
    d)如果向饮料中增加珍珠,则额外要加多2元。
    e)又加珍珠又加砂糖,10+2+1 = 13。

    namespace nmsp2
    {
    	//抽象饮料类
    	class Beverage
    	{
    	public:
    		virtual int getprice() = 0;  //获取价格
    	public:
    		virtual ~Beverage() {}
    	};
    
    	//水果饮料类
    	class FruitBeverage :public Beverage
    	{
    	public:
    		virtual int getprice()
    		{
    			return 10;  //一杯单纯的水果饮料,售价为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;  //额外加多1元,要调用父类的getprice方法以把以往的价格增加进来
    		}
    	};
    
    	//具体的“牛奶”装饰器类
    	class MilkDec :public Decorator
    	{
    	public:
    		MilkDec(Beverage* tmpbvg) :Decorator(tmpbvg) {}  //构造函数
    		virtual int getprice()
    		{
    			return Decorator::getprice() + 2;  //额外加多2元,要调用父类的getprice方法以把以往的价格增加进来
    		}
    	};
    
    	//具体的“珍珠”装饰器类
    	class BubbleDec :public Decorator
    	{
    	public:
    		BubbleDec(Beverage* tmpbvg) :Decorator(tmpbvg) {}  //构造函数
    		virtual int getprice()
    		{
    			return Decorator::getprice() + 2;  //额外加多2元,要调用父类的getprice方法以把以往的价格增加进来
    		}
    	};
    }
    
    int main()
    {
    	//创建一杯单纯的水果饮料,价格10元:
    	nmsp2::Beverage* pfruit = new nmsp2::FruitBeverage();
    	//向饮料中增加珍珠,价格多加了2元
    	nmsp2::Decorator* pfruit_addbubb = new nmsp2::BubbleDec(pfruit);
    	//再向饮料中增加砂糖,价格又加多了1元
    	nmsp2::Decorator* pfruit_addbubb_addsugar = new nmsp2::SugarDec(pfruit_addbubb);
    	//输出最终的价格
    	cout << "加了珍珠又加了砂糖的水果饮料最终价格是:" << pfruit_addbubb_addsugar->getprice() << "元人民币" << endl;
    
    	//释放资源
    	delete pfruit_addbubb_addsugar;
    	delete pfruit_addbubb;
    	delete pfruit;
    
    	std::cout << "主线程执行完毕\n";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    奶茶与水果饮料的装饰模式UML图
    奶茶与水果饮料的装饰模式UML图

  • 相关阅读:
    QT/QML国际化:中英文界面切换显示(cmake方式使用)
    什么是阻塞队列?如何使⽤阻塞队列来实现⽣产者-消费者模型?哪个阻塞队列最常用?
    网络安全(黑客)—自学笔记
    Python实战系列-获取单页和多页京东评论
    自动控制原理5.4---稳定裕度
    MySql生成ER【StarUML】文件
    Rust(trait、Box指针、自动化测试、迭代器)
    [Java反序列化]—Shiro反序列化(二)
    Xiaojie雷达之路---匹配滤波器
    docker私有仓库registry
  • 原文地址:https://blog.csdn.net/zzyzxb/article/details/125566867