0
点赞
收藏
分享

微信扫一扫

C++设计模式 装饰器模式


文章目录

  • ​​1. 先验知识​​
  • ​​2. 装饰器模式相关概念​​
  • ​​3. 装饰器模式的应用​​
  • ​​3.1 应用1: 给形状添加新的特征 不同形状加红​​
  • ​​3.2 应用2: 给一个人穿不同的衣服​​
  • ​​a 直接person类, 增加穿不同衣服的方法​​
  • ​​b. 把person类拆解, 里面的衣服分别写成类的形式​​
  • ​​核心关键最终的代码和逻辑​​
  • ​​c 装饰器模式下的穿衣系统设计​​


学习设计模式的主要目的是为了学习整个软件开发的思维逻辑, 这是上百年程序员大佬们总结出来的精华, 就像学好数理化走遍天下都不怕是一个逻辑, 我永远相信, 业务开发跟设计模式息息相关, 个人思维逻辑与算法息息相关, 活到老, 学到老, 与君共勉即努力.

  • 平台 win10子系统 ​​Ubuntu18.04​
  • 编译器 ​​gcc dbg​
  • 编辑器 ​​VScode​

1. 先验知识

  • 子类继承父类初始化父类: ​​子类构造函数(参数) : 父类构造函数(父类构造函数参数){}​
  • 子类使用父类中的成员: ​​父类名::父类成员​

2. 装饰器模式相关概念

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

按照人话讲: 就是先实例化一个对象, 在给这个对象增加新的功能, 比继承更加灵活的操作, 给一个new出来的对象披上一层外衣

意图: 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,同时继承装饰者模式。

关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。

应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

注意事项:可代替继承。

3. 装饰器模式的应用

3.1 应用1: 给形状添加新的特征 不同形状加红

创建形状, 拥有方形和圆形, 又想要给方形和圆形染色为红色, 按照以往的逻辑, 需要给方形和圆形在增加对应的子类, 在子类里面增加红色, 但是在这里, 我们尝试换一种思路, 增加一个形状子类命名为抽象装饰器类, 里面创建对应的形状对象, 这样当传入的不管是圆还是方形, 都在这个类里面增加染为红色的方法, 这样就实现了装饰器的功能

C++设计模式 装饰器模式_c++

#include <iostream>
#include <algorithm>
using namespace std;

// 创建形状抽象类
class Shape{
public:
virtual void draw() = 0;
};

// 创建圆类
class Rectangle : public Shape{
public:
void draw(){
cout << "create Rectangle ... " <<endl;
}
};

// 创建方类
class Circle : public Shape{
public:
void draw(){
cout << "create Circle ... " <<endl;
}
};

// 创建修饰器的抽象类, 里面先包含一个形状, 然后给这个形状后面再添加功能
class ShapeDecorator : public Shape{
protected:
Shape *decoratedShape = nullptr; // 创建一个基础形状, 并在修饰器初始化时初始化这个形状

public:
ShapeDecorator(Shape *decoratedShape): decoratedShape(decoratedShape){}
virtual void draw()=0;
};

// 创建扩展了ShapeDecorator类的实体装饰类 将形状颜色变为红色
class RedShapeDecorator : public ShapeDecorator{
public:
// 显示的初始化父类的成员变量
RedShapeDecorator(Shape *decoratedShape) : ShapeDecorator(decoratedShape){}

void draw(){
ShapeDecorator::decoratedShape->draw(); // 显式使用父类成员变量, 好像没啥用
setRedBorder(ShapeDecorator::decoratedShape); // 然后给在调用增加功能也就是red颜色
}

private:
void setRedBorder(Shape *decoratedShape){
cout << "border color : rrred" <<endl;
}
};
int main(int argc, char const *argv[])
{
Shape *circle = new Circle(); // 形状创建一个圆, 多态
ShapeDecorator *redCircle = new RedShapeDecorator(new Circle()); // 创建一个红圆
ShapeDecorator *redRectangle = new RedShapeDecorator(new Rectangle()); // 创建一个红正方
// Shape *redCircle = new RedShapeDecorator(new Circle());
// Shape *redRectangle = new RedShapeDecorator(new Rectangle());
cout << "实验一: 创建一个普通的圆, 并打印" << endl;
circle->draw();

cout << "实验二: 创建一个带有红边的圆" <<endl;
redCircle->draw();

cout << "实验二: 创建一个带有红边的方" <<endl;
redRectangle->draw();

delete circle;
delete redCircle;
delete redRectangle;
}

  • 结果

实验一: 创建一个普通的圆, 并打印
create Circle ...
实验二: 创建一个带有红边的圆
create Circle ...
border color : rrred
实验二: 创建一个带有红边的方
create Rectangle ...
border color : rrred

3.2 应用2: 给一个人穿不同的衣服

要求: 给人搭配不同服饰的系统, 比如QQ秀, QQ飞车这种
比如给人穿T恤和大裤衩

接下来通过不同的思路实现, 直到找到最优解决方案

a 直接person类, 增加穿不同衣服的方法

C++设计模式 装饰器模式_设计模式_02


但是如果需求中增加 “超人装饰”?

解决方案: 改一下Persion类即可, 但是这样违背了开放-封闭原则

b. 把person类拆解, 里面的衣服分别写成类的形式

C++设计模式 装饰器模式_子类_03


这样做, 如果需要超人就增加个超人的子类即可

新的问题: 形象展示过程中, 每穿一件衣服, 就需要形象展示, 有一种众目睽睽之下脱衣服的意思, 所以能不能通过内部组装完毕再显示, (这里涉及到建造模式), 但是非也, 因为建造过程是不稳定的, 这里可能是先穿西装, 再穿T恤等, 通俗来讲, 通过服饰组合, 展现一个有个性的人有无数种方案, 并非是固定的

因此: 我们需要把所有需要的功能按照正确的顺序串联起来进行控制

核心关键最终的代码和逻辑

首先给出装饰器模式的逻辑代码, 也可以运行成功

C++设计模式 装饰器模式_装饰器模式_04

  • ​Component​​是定义一个对象接口, 可以给这些对象动态添加职责
  • ​ConcreteComponent​​是定义了一个具体对象, 也可以给对象添加一些职责
  • ​Decorator​​​ 装饰器抽象类, 继承​​Component​​​, 从外类来扩展​​Component​​​ 的功能, 但是对于 ​​Component​​​来讲, 无需知道​​Decorator​​ 的存在
  • ​ConcreteDecorator​​​具体修饰对象, 起到给 ​​Component​​ 添加职责的功能

上述装饰器模式的实现逻辑

#include <iostream>
#include <algorithm>
using namespace std;

// 抽象类
class Component{
public:
virtual void Operation()=0;
};

// 实现
class ConcreteComponent : public Component{
public:
void Operation(){
cout << "原始对象 具体对象的操作/功能" << endl;
}
};

// 抽象装饰器
class Decorator : public Component{
protected:
Component *component = nullptr;
public:
void SetComponent(Component *component){
this->component = component;
}

virtual void Operation(){
if(component != nullptr){
cout<< "抽象修饰器先运行完 原始对象的功能...." <<endl;
component->Operation();
}
}
};

// 装饰器A
class ConcreteDecoratorA : public Decorator{
private:
string addedState;
public:
void Operation(){
// 先运行原Component的Operation(), 在执行本类的功能, 入addedState, 相当于对原Component进行了装饰
Decorator::Operation();
addedState = "大爷";
cout << "具体修饰对象A的操作" <<endl;
}
};

// 装饰器B
class ConcreteDecoratorB : public Decorator{
public:
void Operation(){
// 先运行原Component的Operation(), 在执行本类的功能, 入addedState, 相当于对原Component进行了装饰
Decorator::Operation();
cout << "具体修饰对象B的操作" <<endl;
addedBehavior();
}
void addedBehavior(){
cout << "来跟我一起跳舞" <<endl;
}
};

int main(int argc, char const *argv[])
{
ConcreteComponent *c = new ConcreteComponent();

// Decorator *d1 = new ConcreteDecoratorA();
// Decorator *d2 = new ConcreteDecoratorB();
ConcreteDecoratorA *d1 = new ConcreteDecoratorA();
ConcreteDecoratorB *d2 = new ConcreteDecoratorB();

cout<< "给原始对象包装修饰A" <<endl;
d1->SetComponent(c); // A修饰器包装c
cout<< "给原始对象包装修饰B" <<endl;
d2->SetComponent(d1); // 再用B修饰器包装c
cout<< "输出所有的对象操作情况" <<endl;
d2->Operation(); // 最后在执行结果

delete c, d1, d2;
return 0;
}

  • 输出如下

给原始对象包装修饰A
给原始对象包装修饰B
输出所有的对象操作情况
抽象修饰器先运行完 原始对象的功能....
抽象修饰器先运行完 原始对象的功能....
原始对象 具体对象的操作/功能
具体修饰对象A的操作
具体修饰对象B的操作
来跟我一起跳舞

基于上面的逻辑, 对于穿衣系统的装饰器模式实现

c 装饰器模式下的穿衣系统设计

#include <iostream>
#include <algorithm>
using namespace std;

// Person类
class Persion{
public:
// Persion() : Persion("zjq"){}
Persion(){}
Persion(string name):name(name){}
~Persion(){}
virtual void show(){
cout << ":装扮的"<< name <<endl;
}

private:
string name;
};

// 服饰类, 装饰器, 需要继承persion
class Finery : public Persion{
protected:
Persion *component = nullptr; // 裸体人

public:
Finery(){}
~Finery(){}

// 传入一个人, 给这个人打扮一番
void Decorator(Persion *component){
this->component = component;
}

virtual void show(){
// 这个人展示
if(component != nullptr){
component->show();
}else{
cout << "还没有领人进入试衣间..." << endl;
}
}

};

// 具体服饰类
class Tshirts : public Finery{
public:
// 初始化父类成员
Tshirts(){}
void show(){
// cout << Decorator::component->show() << "大体恤" << endl;
cout << "大体恤 ";
Finery::show();
}
};

class BigTrouser : public Finery{
public:
// 初始化父类成员
BigTrouser(){}
void show(){
// cout << Decorator::component->show() << "大体恤" << endl;
cout << "大裤衩 ";
Finery::show();
}
};

int main(int argc, char const *argv[])
{
Persion *xm = new Persion("小明");
// Tshirts *tx = new Tshirts();
// BigTrouser *bt = new BigTrouser();
Finery *tx = new Tshirts();
Finery *bt = new BigTrouser();

tx->Decorator(xm); // 给小明穿上tx
bt->Decorator(tx); // 给穿了tx的小明在穿上大裤衩子
bt->show();

tx->Decorator(xm);
bt->Decorator(tx);
bt->show();

delete xm, tx, bt;
return 0;
}

  • 输出如下

大裤衩 大体恤 :装扮的小明
大裤衩 大体恤 :装扮的小明


举报

相关推荐

0 条评论