C++设计模式之装饰器模式(decorator)(结构型)

一、模式动机

比如,给自家宠物小狗的画框需要不同的颜色等。

C++设计模式之装饰器模式(decorator)(结构型)_第1张图片

一般有两种方式可以实现给一个类或对象增加行为:

        • 继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。

        • 关联机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)。

    装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。这就是装饰模式的模式动机。 装饰器模式提供了改变子类的灵活方案。装饰器模式在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。当用于一组子类时,装饰器模式更加有用。如果你拥有一族子类(从一个父类派生而来),你需要在与子类独立使用情况下添加额外的特性,你可以使用装饰器模式,以避免代码重复和具体子类数量的增加。

二、模式定义

装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。

三、ULM图:

这里写图片描述

装饰模式包含如下角色:

Component抽象构件:定义我们最核心的对象的一个接口或者是抽象类,即最原始的对象(必然有一个最基本、最核心、最原始的接口或抽象类充当Component抽象构件)

ConcreteComponent 具体构件:定义一个要被装饰器装饰的对象,Component 的具体实现,即被装饰者。

抽象Decorator装饰角色:一般是一个抽象类(便于根据不同的装饰逻辑去实现其子类)且一定会持有一个private变量指向Component抽象构件的引用(它里面不一定只有抽象的方法呀)维护对组件对象和其子类组件的引用,但是要注意的是所谓装饰者仅仅是发挥锦上添花的作用,核心的本质功能还是应该由构件提供,相当于是把在构件的基础上进行升级,所需要继承构件。

具体装饰器角色(ConcreteDecorator):通常是一群子装饰器组合共同为组件添加新的功能。

四、装饰器模式的优点和缺点及可用场景

1、装饰器模式的优点
虽然装饰模式与继承关系的都是为了要在基类的基础上扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性,装饰模式允许系统动态决定“贴上”或者除掉一个“装饰”而不需要改变代码的层次;而继承关系是静态的,如果想要增加或者减少一个功能只能通过增加继承层次或降低层次。

使用者可以随时根据具体的业务逻辑灵活组织所需要的功能,通过使用不同的具体装饰类以及这些装饰类的组合即可

装饰者类可以在被装饰者的行为前面或后面加上自己的行为,甚至取代被装饰者的行为

装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型

通过使用装饰器模式,我们可以实现不改变原有代码,开放现有代码的方式来实现更多的功能。

装饰类和被装饰类是可以独立发展且不会相互耦合。即Component类无须知
道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具
体的构件。

2、装饰器模式的缺点
使用装饰模式会产生比使用继承关系更多的对象和复杂的逻辑,降低了可读性,因此,合理使用装饰类,控制其数量,以便降低系统的复杂度

3、装饰器模式的适用场景及注意事项

  • 在不影响其他对象的情况下,需要以动态、透明的方式给随时给被装饰对象添加或者移除某些功能时而不影响原有逻辑,想通过灵活组合对象的方式动态地改变被装饰对象的行为
  • 需要扩展一个类的功能或给一个类增加或随时移除附加功能。
  • 当需要以多层次的继承关系才能实现扩充时,可以考虑装饰器模式。
  • 需要为某一类型的兄弟类进行改装或加装功能,首选装饰模式。
  • 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
  • 装饰对象包含一个真实对象的引用(reference)
  • 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
  • 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。

4、 和建造者模式的区别

建造者模式要求建造的过程必须是稳定的,而装饰模式的建造过程是不稳定的,可以有各种各样的组合方式。

五、实例

5.1 变形金刚

变形金刚在变形之前是一辆汽车,它可以在陆地上移动。当它变成机器人之后除了能够在陆地上移动之外,还可以说话;如果需要,它还可以变成飞机,除了在陆地上移动还可以在天空中飞翔。

C++设计模式之装饰器模式(decorator)(结构型)_第2张图片

#include 
#include 

//(compoent)
class Transform 
{
public:
	virtual void move() = 0;
};

//具体构件类Car
class Car : public Transform 
{
public:
	Car() 
	{
		std::cout << "变形金刚是一辆车!" << std::endl;
	}

	void move() 
	{
		std::cout << "在陆地上移动。" << std::endl;
	}
};

//抽象装饰类
class Changer : public Transform 
{
public:
	Changer(std::shared_ptr transform) 
	{
		this->transform = transform;
	}
	void move() 
	{
		transform->move();
	}
private:
	std::shared_ptr transform;
};

//具体装饰类Robot
class Robot : public Changer 
{
public:
	Robot(std::shared_ptr transform) : Changer(transform)
	{
		std::cout << "变成机器人!" << std::endl;
	}

	void say() 
	{
		std::cout << "说话!" << std::endl;
	}
};

//具体装饰类AirPlane
class Airplane : public Changer 
{
public:
	Airplane(std::shared_ptr transform) : Changer(transform)
	{
		std::cout << "变成飞机!" << std::endl;
	}

	void fly() 
	{
		std::cout << "在天空飞翔!" << std::endl;
	}
};

//客户端测试 
int main(void) 
{
	auto camaro = std::make_shared();
	camaro->move();
	std::cout << "--------------" << std::endl;
	auto bumblebee = std::make_shared(camaro);
	bumblebee->move();
	bumblebee->say();
	auto air = std::make_shared(camaro);
	air->fly();

	return 0;
}

结果如下:

C++设计模式之装饰器模式(decorator)(结构型)_第3张图片

5.2 手机

有一个手机,允许你为手机添加特性,比如增加挂件、屏幕贴膜等。

#include 
#include 
//公共抽象类
class Phone
{
public:
	Phone() = default;
	virtual ~Phone() = default;
	virtual void ShowDecorate() = 0;
};
//具体的手机类:苹果手机
class iPhone : public Phone
{
private:
	std::string m_name; //手机名称
public:
	iPhone(std::string name) : m_name(name)
	{}
	~iPhone() = default;
	void ShowDecorate() 
	{ 
		std::cout << m_name << "的装饰" << std::endl; 
	}
};
//具体的手机类
class HuaWeiPhone : public Phone
{
private:
	std::string m_name;
public:
	HuaWeiPhone(std::string name) : m_name(name) {}
	~HuaWeiPhone() = default;
	void ShowDecorate() 
	{ 
		std::cout << m_name << "的装饰" << std::endl; 
	}
};

//装饰类
class DecoratorPhone : public Phone
{
private:
	Phone* m_phone;  //要装饰的手机
public:
	DecoratorPhone(Phone* phone) : m_phone(phone) {}
	virtual void ShowDecorate() 
	{ 
		m_phone->ShowDecorate(); 
	}
};
//具体的装饰类
class DecoratorPhoneA : public DecoratorPhone
{
public:
	DecoratorPhoneA(Phone* phone) : DecoratorPhone(phone) {}
	void ShowDecorate() { DecoratorPhone::ShowDecorate(); AddDecorate(); }
private:
	void AddDecorate() { std::cout << "增加挂件" << std::endl; } //增加的装饰
};
//具体的装饰类
class DecoratorPhoneB : public DecoratorPhone
{
public:
	DecoratorPhoneB(Phone* phone) : DecoratorPhone(phone) {}
	void ShowDecorate() { DecoratorPhone::ShowDecorate(); AddDecorate(); }
private:
	void AddDecorate() { std::cout << "屏幕贴膜" << std::endl; } //增加的装饰
};

int main()
{
	//HuaWei
    {
	    auto phone = std::make_shared("Mate30");
	    auto dpa = std::make_shared < DecoratorPhoneA>(phone.get()); //装饰,增加挂件
	    auto dpb = std::make_shared < DecoratorPhoneB>(dpa.get());    //装饰,屏幕贴膜
	    dpb->ShowDecorate();
    }
    std::cout << "---------------------" << std::endl;
    //iPhone
    {
	    auto phone = std::make_shared("iPhone13");
	    auto dpa = std::make_shared < DecoratorPhoneA>(phone.get()); //装饰,增加挂件
	    auto dpb = std::make_shared < DecoratorPhoneB>(dpa.get());    //装饰,屏幕贴膜
	    dpb->ShowDecorate();
    }
	
	return 0;
}

结果如下

六、模式扩展

装饰模式的简化-需要注意的问题

    • 一个装饰类的接口必须与被装饰类的接口保持相同,对于客户端来说无论是装饰之前的对象还是装饰之后的对象都可以一致对待。

    • 尽量保持具体构件类Component作为一个“轻”类,也就是说不要把太多的逻辑和状态放在具体构件类中,可以通过装饰类对其进行扩展。

    • 如果只有一个具体构件类而没有抽象构件类,那么抽象装饰类可以作为具体构件类的直接子类。

装饰模式的简化

C++设计模式之装饰器模式(decorator)(结构型)_第4张图片

以下转自:https://www.cnblogs.com/jenkinschan/p/5683099.html

一、概述

  装饰器模式动态地将责任附加到对象上。想要扩展功能,装饰者提供了有别于继承的另一种选择。简单描述就是包装对象,让对象提供新的行为。

二、解决问题

  当一个类想要获得一个行为,我们会想到面向对象四大特性之一的继承,继承能够让子类从父类中获得行为,实现很好的代码复用。但这种继承而来的行为是在编译时静态决定的,而且所有的子类都会继承相同的行为。如果我们想要扩展对象的行为,就要创建一个子类来修改父类的方法(也就是覆盖父类行为),每扩展一个行为就要创建一个子类,这样会带来很多问题。第一,如果需要扩展的行为有很多,则子类就要创建无数多个(导致类爆炸)。第二、如果子类中的行为需要依赖某个成员变量,当这个成员变量发生改变,子类的代码就要修改(代码维护噩梦)

C++设计模式之装饰器模式(decorator)(结构型)_第5张图片

 如上图,getCost()方法获取茶的价钱,我们每新上市一种茶饮料就要创建一个子类;当茶的某些配料价格发生变化时,我们要修改子类代码。

装饰者模式就是解决以上的问题的,它利用组合的做法扩展对象行为,可以在运行时动态地进行扩展,写新的代码添加新功能,而无须修改现有代码。

 三、结构类图

C++设计模式之装饰器模式(decorator)(结构型)_第6张图片

四、成员角色

   1.抽象组件(Component)角色:定义一个将要接收附加责任的类,即继承该抽象类的类都有了装饰和被装饰的能力。

  2.具体组件(ConcreteComponent)角色:可以被动态加上新行为,被装饰者修饰的类。

  3.装饰者(Decorator)角色:装饰者抽象类,继承该类都具有装饰者的能力。

  4.具体装饰者(ConcreteDecorator)角色:为具体组件添加新行为。

五、应用实例

下面我们用奶茶饮料价格的例子来解析装饰者的用法,买奶茶的时候我们可以可以买纯奶茶,也可以添加珍珠,咖啡等配料,但价格肯定不一样

  //首次我们创建抽象组件,也就是茶饮料

1

2

3

4

5

6

7

8

9

public abstract class Tea {

    String description = "Unknown Tea";

    //茶的描述

    public String getDescription(){

        return description;

    }

    //返回茶的价钱

    public abstract double getCost();

}

  //接着创建抽象配料,就是装饰者

1

2

3

4

public abstract class CondimentDecorator extends Tea{

    //所有调料必须重新实现描述方法

    public abstract String getDescription();

}

   //创建奶茶,具体组件,继承抽象组件,被装饰的对象

1

2

3

4

5

6

7

8

9

10

11

12

13

public class MilkTea extends Tea{

    public MilkTea(){

        description = "奶茶";

    }

     

    @Override

    public double getCost() {

        //返回奶茶价格

        return 3.0;

    }

}

  //创建珍珠配料,就是具体的装饰者,继承抽象配料,实现对奶茶的装饰

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public class Pearl extends CondimentDecorator{

    //持有对所装饰对象的引用

    private Tea tea;

     

    public Pearl(Tea tea){

        this.tea = tea;

    }

     

    @Override

    public String getDescription() {

        return "珍珠," +  tea.getDescription() ;

    }

    @Override

    public double getCost() {

        //把茶的价格加上珍珠的价格,得到最后结果

        return 1.0 + tea.getCost();

    }

}

  //创建咖啡配料,就是具体的装饰者,继承抽象配料,实现对奶茶的装饰

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public class Coffee extends CondimentDecorator{

    //持有对所装饰对象的引用

    private Tea tea;

     

    public Coffee(Tea tea){

        this.tea = tea;

    }

     

    @Override

    public String getDescription() {

        //加上咖啡后的描述

        return "咖啡," + tea.getDescription();

    }

    @Override

    public double getCost() {

        //茶的价格加上咖啡的价格,算出结果

        return 2.0 + tea.getCost();

    }

}

  //测试装饰者

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public class TestDecorator {

    public static void main(String[] args){

        //创建一杯纯奶茶,不需要加调料,打印出描述和价格

        Tea tea = new MilkTea();

        System.out.println(tea.getDescription() + " 价格为:¥" + tea.getCost());

         

        //创建加调料的奶茶

        Tea tea2 = new MilkTea();

        //加上一份珍珠

        tea2 = new Pearl(tea2);

        //再加一份珍珠

        tea2 = new Pearl(tea2);

        //加上一份咖啡调料

        tea2 = new Coffee(tea2);

         

        //打印加了调料的奶茶

        System.out.println(tea2.getDescription() + " 价格为:¥" + tea2.getCost());

    }

}

  运行结果:

你可能感兴趣的:(C++,设计模式,设计模式,装饰模式,decorator)