设计模式——装饰模式

1. 概述

  有时我们希望给某个对象而不是整个类添加一些功能。例如,一个图形用户界面工具箱允许你对任意一个用户界面添加一些特性,例如边框,或是一些行为,例如窗口滚动。
  使用继承机制是添加功能的一种有效途径,从其他类继承过来的边框特性可以被多个子类的实例所使用。但这种方法不够灵活,因为边框的选择是静态的,用户不能控制对组件加边框的方式和时机。
  一种较为灵活的方式是将组件嵌入另一个对象中,由这个对象添加边框。我们称这个嵌入的对象为 装饰。这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。它将客户请求转发给该组件,并且可能在转发前后执行一些额外的动作(例如画一个边框)。透明性使得你可以递归的嵌套多个装饰,从而可以添加任意多的功能。
   装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

2. 结构图

  下面是装饰模式的结构图:

  • Component:定义一个对象接口,可以给这些对象动态的添加职责。
  • ConcreteComponent:定义一个对象,可以给这个对象添加一些职责。
  • Decorator:维持一个指向Component 对象的指针,并定义一个与Component 接口一致的接口。
  • ConcreteDecorator:向组件添加职责,
  Decorator将请求转发给它的Component对象,并且可能在转发请求后执行一些附加的动作。

3. 实例分析

  考虑这么一个应用场景:假定有一个对象TextView,它可以在窗口中显示正文。缺省的TextView没有滚动条,因为我们可能有时候不需要滚动条。当需要滚动条时,我们可以用ScrollDecorator添加滚动条。如果我们还想在TextView周围添加一个粗黑边框,可以使用BorderDecorator添加。因此只要简单地将这些装饰和TextView进行组合没救可以达到预期的效果。
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  VisualComponent.h
*Author:  Huangjh
*Version:
*Date:  2017-11-13
*Description:  相当于结构图中的Component角色——定义一个对象接口
*Others:
**********************************************************************************/
#ifndef _VISUAL_COMPONENT_H
#define _VISUAL_COMPONENT_H

class CVisualComponent
{
public:
	CVisualComponent() { }
	virtual ~CVisualComponent() { }

	virtual void Draw(void) = 0;
};

#endif	//#ifndef _VISUAL_COMPONENT_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  TextView.h
*Author:  Huangjh
*Version:
*Date:  2017-11-13
*Description:  相当于结构图中的ConcreteComponent角色——定义一个对象,我们的目的就是
				给这个对象添加一些职责
*Others:
**********************************************************************************/
#ifndef _TEXT_VIEW_H
#define _TEXT_VIEW_H

#include <iostream>
#include "VisualComponent.h"

class CTextView : public CVisualComponent
{
public:
	CTextView() { }
	~CTextView() { }

	void Draw(void)
	{
		std::cout << "在窗口中显示正文" << std::endl;
	}
};

#endif	//#ifndef _TEXT_VIEW_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  Decorator.h
*Author:  Huangjh
*Version:
*Date:  2017-11-13
*Description:  相当于结构图中的Decorator角色——维持一个指向Component对象的指针
				并定义一个与Component接口一致的接口
*Others:
**********************************************************************************/
#ifndef _DECORATOR_H
#define _DECORATOR_H

#include <cstddef>
#include "VisualComponent.h"

class CDecorator : public CVisualComponent
{
public:
	CDecorator(CVisualComponent *pComponent)
		:m_pComponent(pComponent)
	{ 
	
	}
	virtual ~CDecorator() { }

	virtual void Draw(void)
	{
		if (m_pComponent != NULL)
			m_pComponent->Draw();
	}

private:
	CVisualComponent *m_pComponent;
};

#endif	//#ifndef _DECORATOR_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  ScrollDecorator.h
*Author:  Huangjh
*Version:
*Date:  2017-11-13
*Description:  相当于结构图中的ConcreteDecorator角色——向组件添加一个滚动条的职责
*Others:
**********************************************************************************/
#ifndef _SCROLL_DECORATOR_H
#define _SCROLL_DECORATOR_H

#include <iostream>
#include "Decorator.h"

class CScrollDecorator : public CDecorator
{
public:
	CScrollDecorator(CVisualComponent *pComponent, int iWidth)
		:CDecorator(pComponent)
		, m_iWidth(iWidth)
	{
		
	}
	~CScrollDecorator() { }

	void Draw(void)
	{
		CDecorator::Draw();
		DrawScroll();
	}

private:
	void DrawScroll(void)
	{
		std::cout << "绘制一个宽度为" << m_iWidth << "的滚动条" << std::endl;
	}

	int m_iWidth;
};

#endif	//#ifndef _SCROLL_DECORATOR_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  BorderDecorator.h
*Author:  Huangjh
*Version:
*Date:  2017-11-13
*Description:  相当于结构图中的ConcreteDecorator角色——向组件添加一个边框的职责
*Others:
**********************************************************************************/
#ifndef _BORDER_DECORATOR_H
#define _BORDER_DECORATOR_H

#include <iostream>
#include "Decorator.h"

class CBorderDecorator : public CDecorator
{
public:
	CBorderDecorator(CVisualComponent *pComponent, int iWidth)
		:CDecorator(pComponent)
		,m_iWidth(iWidth)
	{
		
	}
	~CBorderDecorator() { }

	void Draw(void)
	{
		CDecorator::Draw();
		DrawBorder();
	}

private:
	void DrawBorder(void)
	{
		std::cout << "绘制一个宽度为" << m_iWidth << "的边框" << std::endl;
	}

	int m_iWidth;
};

#endif	//#ifndef _BORDER_DECORATOR_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  main.cpp
*Author:  Huangjh
*Version:
*Date:  2017-11-13
*Description:  装饰模式的测试用例
*Others:
**********************************************************************************/
#include "TextView.h"
#include "ScrollDecorator.h"
#include "BorderDecorator.h"

int main(void)
{
	CVisualComponent *pTextView = new CTextView();
	CVisualComponent *pScrollDecorator = new CScrollDecorator(pTextView, 2);
	CVisualComponent *pBorderDecorator = new CBorderDecorator(pScrollDecorator, 3);

	pBorderDecorator->Draw();

	return 0;
}
  运行结果如下所示:
在窗口中显示正文
绘制一个宽度为2的滚动条
绘制一个宽度为3的边框
  当你仅需要添加一个职责时,没有必要定义抽象CDecorator类。你常常需要处理现存的类层次结构而不是设计一个新系统,这时你可以把CDecorator向Component转发请求的职责合并到ConcreteDecorator中。

4. 优缺点

4.1 优点

  • 比静态继承更灵活 与对象的静态继承(多重继承)相比,Decorator模式提供了更加灵活的向对象添加职责的方式。可以用添加和分离的方法,用装饰在运行时刻增加和删除职责。相比之下,继承机制要求为每个添加的职责创建一个新的子类。这会产生许多新的类,并且会增加系统的复杂度。此外,为一个特定的Component类提供多个不同的Decorator类,这就使得你可以对一些职责进行混合和匹配。
  • 避免在层次结构高层的类有太多的特征 Decorator模式提供了一种“即用即付”的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以定义一个简单的类,并用Decorator类给它逐渐地添加功能。可以从简单的部件组合出复杂的功能。

4.2 缺点

  • Decorator与它的Component不一样 Decorator是一个透明的包装。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰时不应该依赖对象标识。
  • 有许多小对象 采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象。这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。

5. 适用性

  以下情况使用Decorator模式
  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  • 处理那些可以撤销的职责。
  • 当不能采用生成子类的方法进行扩充时。一种情况是,可以有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

6. 参考资料

  • 《大话设计模式》
  • 《设计模式——可复用的面向对象软件的基础》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值