文章目录
- 前言
- 1. 原理阐述
- 2. 举例
- 2.1 人装饰方案一
- 2.2 人装饰方案二
- 2.3 人装饰方案三
- 总结
前言
近期在给一个已有的功能拓展新功能时,基于原有的设计类图进行讨论。其中涉及到了装饰模式,因为书本很早已经看过一遍,所以谈及到这个名词的时候有点印象,只知道它是加功能用的,更细则的内容已经忘了。
这篇博客就是对装饰模式的一个复习。
1. 原理阐述
装饰模式:
动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
前半句话:一个类的设计在一开始并不会是非常完美的,虽然一开始做到了单一职责的原则,但是随着业务的扩大功能的不断开发,难免会出现多职责的情况。那么在后期添加新功能的时候,如果要保持原来类的核心职能尽可能的单一,而非和一些新加功能糅杂在一起,装饰模式就是解决这个问题的设计模式。
后半句话:我们可以给一个类派生子类,通过子类添加新功能。子类实际上还是依赖于父类,是is a的关系。增加新功能这个点,我们能不能想象成给人穿衣服的过程?将功能模块化,加不加功能,这个功能就在那,需要这个功能的时候,将功能和类进行绑定(装饰 )。衣服,人穿不穿它都在那(功能解耦),人穿就是装饰(绑定)。
上面的是我个人的理解,如有不对,留言指正。
2. 举例
以书中穿衣的例子为例,结合自己理解拓展。
2.1 人装饰方案一
把人比作一个类Person的对象,人穿衣服,其实就是一些函数操作。
那么简单一点的类图如下:
这个方案不合理。因为如果要增加新的装饰的话,就需要修改人这个类。
设计模式学习[3]—单一职责原则+开放封闭原则中我提到了开放封闭原则,那么我们修改人这个类,是不是违反了开放封闭原则?比如我添加穿袜子
这个装饰操作,本质上它应该是对Person类的一个拓展,在开放封闭原则中,对于更改是封闭的,对于拓展是开放的。如果按照我们这个简单的类图来做,我们就需要修改Person类,就违反了开放封闭原则。
由此,我们考虑把这个装饰的过程抽象出来,在Person类中,用统一的一个接口去调用。
2.2 人装饰方案二
于是乎,就有了下面的类图。
这张类图里面,我们把人的装饰过程用形象展示
这个接口包装起来。具体的装饰交给服饰类去处理。
这样我们要拓展,对于人这个类,其实没有改动。要拓展其实是对服饰类进行拓展。
我们这里通过面向对象的方式,将每一个装饰操作由具体的子类去负责。通过接口继承,多态实现。
现在我们思考一下这个服饰
类和人
类之间的关系。
回顾一下原理阐述,拓展新功能可以通过子类继承以及装饰模式,但是装饰模式更灵活(因为用的绑定)。
所以这里在类图的实际表现来说,服饰类其实应该是对人的一个实现。
在C++中,无论是继承还是实现,其实都是
实线——加△
实线——加△
实线——加△的画法。实现的方式在代码中是public继承,继承中就可以有public,protected,private几种继承的考量了,这里不深究。
2.3 人装饰方案三
知道了人和服饰类之间的关系,那么就有了下面的类图
从设计层面明白后,看一下具体代码.
对象的绑定关系其实是通过指针来实现的,服饰类的构造函数接受一个Person类的对象,作为绑定对象。后面的T恤类以及夹克类都是对这个绑定对象的一个装饰。
这里mian函数中出现的两种装饰方式,其实说明的是有些功能是有先后顺序的,比如先有数据接收功能再有数据处理功能,先收到数据才对数据进行处理。
#include <iostream>
#include <string>
class Person
{
public:
Person() {};
Person(std::string name)
{
this->name = name;
}
virtual void Show()
{
std::cout << "装扮的" << this->name;
}
private:
std::string name;
};
//服饰类
class Finery :public Person
{
protected:
Person* component;
public:
void Decorate(Person* component)
{
this->component = component;
}
void Show() override
{
if (component)
{
component->Show();
}
}
};
//服饰的具体类:T恤
class TShirts :public Finery
{
public:
void Show() override
{
printf("T恤 ");
Finery::Show();
}
};
//服饰的具体类:夹克
class Jacket :public Finery
{
public:
void Show() override
{
printf("夹克 ");
Finery::Show();
}
};
int main()
{
Person *cc = new Person("澄澈i");
std::cout << "第一种装饰" << std::endl;
TShirts* tshirt = new TShirts();
Jacket* jacket = new Jacket();
tshirt->Decorate(cc);
jacket->Decorate(tshirt);
jacket->Show();
std::cout <<std::endl;
std::cout << "第二种装饰" << std::endl;
jacket->Decorate(cc);
tshirt->Decorate(jacket);
tshirt->Show();
delete cc;
delete tshirt;
delete jacket;
return 0;
}
总结
装饰模式看书的时候看的挺快的,但是想用自己的话写出来,还是得好好想想。
这篇博客对装饰模式做了一个自我理解的阐述,结合书中的例子进行分阶段设计,最后到代码的具体展现。
收获尚可。