装饰模式
定义
动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活
动机
一般有两种方式可以实现给一个类或对象增加行为:
- 继承机制,使用继承机制是给现有类添加功能的一种有效途径,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。
- 关联机制,即将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为,我们称这个嵌入的对象为装饰器(Decorator)
装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。这就是装饰模式的模式动机。
UML类图
场景拆解
按照峡谷男人的逻辑,我们把装饰模式和王者荣耀的一些逻辑联合起来。是不是很简单的就想到了王者荣耀里面的星元皮肤,在星元皮肤模块里面我们可以自定义英雄的皮肤组合方式,从而装饰出来峡谷第一无二的装扮,驰骋峡谷变成峡谷最靓的仔~
话不多说,我们来拆解一下这个过程:
-
抽象待装饰类
- 英雄Hero
-
具体待装饰类
- 香香Xiangxiang
-
装饰星元皮肤的基类
- DecoratorSkin
-
具体的星元配件类
-
香香的手持枪炮DecoratorGuns
-
香香的发型DecoratorHairstyle
-
香香的风衣DecoratorWindbreaker
-
香香的长靴DecoratorLongBoots
-
Ok,现在编写代码给峡谷后期超强射手大小姐装上拉风的造型,"大小姐驾到,通通闪开~":
源码实现
- hero.h
#ifndef HERO_H
#define HERO_H
class Hero
{
public:
Hero(){}
virtual ~Hero() {}
//造型
virtual void Modeling() = 0;
};
class Xiangxiang : public Hero
{
public:
Xiangxiang();
virtual ~Xiangxiang() override;
virtual void Modeling() override;
};
#endif // HERO_H
- hero.cpp
#ifndef HERO_H
#define HERO_H
class Hero
{
public:
Hero(){}
virtual ~Hero() {}
//造型
virtual void Modeling() = 0;
};
class Xiangxiang : public Hero
{
public:
Xiangxiang();
virtual ~Xiangxiang() override;
virtual void Modeling() override;
};
#endif // HERO_H
- decoratorskin.h
#ifndef DECORATORSKIN_H
#define DECORATORSKIN_H
#include "hero.h"
class DecoratorSkin : public Hero
{
public:
DecoratorSkin(Hero* hero = nullptr);
virtual ~DecoratorSkin() override;
virtual void Modeling() override;
virtual void UpdateModeling();
void SetHero(Hero* hero);
protected:
Hero* m_Hero;
};
class DecoratorGuns : public DecoratorSkin
{
public:
DecoratorGuns(Hero* hero = nullptr);
virtual ~DecoratorGuns() override;
virtual void Modeling() override;
void UpdateGuns1();
void UpdateGuns2();
};
class DecoratorHairstyle : public DecoratorSkin
{
public:
DecoratorHairstyle(Hero* hero = nullptr);
virtual ~DecoratorHairstyle() override;
virtual void Modeling() override;
void UpdateHairstyle1();
void UpdateHairstyle2();
};
class DecoratorWindbreaker : public DecoratorSkin
{
public:
DecoratorWindbreaker(Hero* hero = nullptr);
virtual ~DecoratorWindbreaker() override;
virtual void Modeling() override;
void UpdateWindbreaker1();
void UpdateWindbreaker2();
};
class DecoratorLongBoots : public DecoratorSkin
{
public:
DecoratorLongBoots(Hero* hero = nullptr);
virtual ~DecoratorLongBoots() override;
virtual void Modeling() override;
void UpdateLongBoots1();
void UpdateLongBoots2();
};
#endif // DECORATORSKIN_H
- decoratorskin.cpp
/************************************
* @brief : 皮肤装饰类
* @author : wzx
* @date : 2020-04-16
* @project : Decorator
*************************************/
#include <QDebug>
#include "decoratorskin.h"
DecoratorSkin::DecoratorSkin(Hero* hero)
: m_Hero(hero)
{
}
DecoratorSkin::~DecoratorSkin()
{
}
void DecoratorSkin::Modeling()
{
if(m_Hero)
m_Hero->Modeling();
}
void DecoratorSkin::UpdateModeling()
{
}
void DecoratorSkin::SetHero(Hero *hero)
{
if(m_Hero == hero)
return;
m_Hero = hero;
}
DecoratorGuns::DecoratorGuns(Hero *hero)
: DecoratorSkin(hero)
{
}
DecoratorGuns::~DecoratorGuns()
{
}
void DecoratorGuns::Modeling()
{
m_Hero->Modeling();
UpdateGuns1();
}
void DecoratorGuns::UpdateGuns1()
{
qDebug() << "换上了枪炮1,更加炫酷了";
}
void DecoratorGuns::UpdateGuns2()
{
qDebug() << "换上了枪炮2,更加炫酷了";
}
DecoratorHairstyle::DecoratorHairstyle(Hero *hero)
{
}
DecoratorHairstyle::~DecoratorHairstyle()
{
}
void DecoratorHairstyle::Modeling()
{
m_Hero->Modeling();
UpdateHairstyle2();
}
void DecoratorHairstyle::UpdateHairstyle1()
{
qDebug() << "换上了发型1,更霸气了";
}
void DecoratorHairstyle::UpdateHairstyle2()
{
qDebug() << "换上了发型2,更霸气了";
}
DecoratorWindbreaker::DecoratorWindbreaker(Hero *hero)
{
}
DecoratorWindbreaker::~DecoratorWindbreaker()
{
}
void DecoratorWindbreaker::Modeling()
{
m_Hero->Modeling();
UpdateWindbreaker1();
}
void DecoratorWindbreaker::UpdateWindbreaker1()
{
qDebug() << "换上了风衣1,更拉风了";
}
void DecoratorWindbreaker::UpdateWindbreaker2()
{
qDebug() << "换上了风衣2,更拉风了";
}
DecoratorLongBoots::DecoratorLongBoots(Hero *hero)
{
}
DecoratorLongBoots::~DecoratorLongBoots()
{
}
void DecoratorLongBoots::Modeling()
{
m_Hero->Modeling();
UpdateLongBoots1();
}
void DecoratorLongBoots::UpdateLongBoots1()
{
qDebug() << "换行了长筒靴1,更有女人味儿了";
}
void DecoratorLongBoots::UpdateLongBoots2()
{
qDebug() << "换行了长筒靴2,更有女人味儿了";
}
- main.cpp
#include <QCoreApplication>
#include "hero.h"
#include "decoratorskin.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Hero* xiangxiang = new Xiangxiang();
DecoratorSkin* dec1 = new DecoratorHairstyle();
DecoratorSkin* dec2 = new DecoratorGuns();
DecoratorSkin* dec3 = new DecoratorWindbreaker();
DecoratorSkin* dec4 = new DecoratorLongBoots();
dec1->SetHero(xiangxiang);
dec2->SetHero(dec1);
dec3->SetHero(dec2);
dec4->SetHero(dec3);
dec4->Modeling();
return a.exec();
}
- 运行结果
大小姐驾到,通通闪开~
默认经典皮肤
换上了发型2,更霸气了
换上了枪炮1,更加炫酷了
换上了风衣1,更拉风了
换行了长筒靴1,更有女人味儿了
优点
装饰模式的优点:
- 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
- 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”
缺点
装饰模式的缺点:
- 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
- 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
参考《大话设计模式》和 https://design-patterns.readthedocs.io/zh_CN/latest/index.html