设计模式–装饰器模式
思路
根据父类设计装饰器类,并在使用的时候实现新的子类,每次使用装饰器得到的指针地址都会变化。
优点
1.灵活性:可以在运行时动态地给对象添加功能,而不需要修改对象的原始代码。
2.可维护性:装饰器模式遵循开闭原则,即对扩展开放,对修改关闭。如果要添加新的功能,只需要创建新的装饰器类并实现相应的功能即可,不需要修改已经存在的类的代码。
3.单一原则:每个装饰器类都专注于一个功能的添加,这使得代码的内聚性较高。
设计
首先有一个抽象英雄类,再有一个具体英雄类A、抽象装饰器类全都继承抽象英雄类,最后有一个具体装饰器类继承抽象装饰器类。
这种模式实际上就是用具体装饰器指针拷贝具体英雄类A的数据,并进行一些想要增加的操作,最后由于全都继承抽象英雄类,所以根据多态的使用,都能正常指向。
#include <iostream>
// 抽象的英雄
class AbstractHero
{
public:
virtual void showStatus() = 0;
int mHp;
int mMp;
int mAt;
int mDf;
};
class HeroA : public AbstractHero
{
public:
HeroA()
{
mHp = 0;
mMp = 0;
mAt = 0;
mDf = 0;
}
virtual void showStatus()
{
std::cout << "血量:" << mHp << std::endl;
std::cout << "魔法:" << mMp << std::endl;
std::cout << "攻击:" << mAt << std::endl;
std::cout << "防御:" << mDf << std::endl;
}
};
// 抽象的装备装饰器
class AbstractEquipment : public AbstractHero
{
public:
AbstractEquipment(AbstractHero* hero)
{
this->pHero = hero;
std::cout << "抽象装饰器,被装饰对象地址:" << pHero << std::endl;
}
virtual void showStatus()
{
}
AbstractHero* pHero;
};
// 增加防御的铠甲装饰器
class ArmorEquipment : public AbstractEquipment
{
public:
ArmorEquipment(AbstractHero* hero) : AbstractEquipment(hero) {}
void AddArmorEffect()
{
std::cout << "英雄穿上铠甲之后" << std::endl;
this->mHp = this->pHero->mHp;
this->mMp = this->pHero->mMp;
this->mAt = this->pHero->mAt;
this->mDf = this->pHero->mDf + 30;
}
virtual void showStatus()
{
AddArmorEffect();
std::cout << "血量:" << mHp << std::endl;
std::cout << "魔法:" << mMp << std::endl;
std::cout << "攻击:" << mAt << std::endl;
std::cout << "防御:" << mDf << std::endl;
std::cout << "铠甲装饰器调用" << std::endl;
pHero->showStatus();
}
};
// 增加攻击的武器装饰器
class WeaponEquipment : public AbstractEquipment
{
public:
WeaponEquipment(AbstractHero* hero) : AbstractEquipment(hero) {}
void AddWeaponEffect()
{
std::cout << "英雄装备武器之后" << std::endl;
this->mHp = this->pHero->mHp;
this->mMp = this->pHero->mMp;
this->mAt = this->pHero->mAt + 50;
this->mDf = this->pHero->mDf;
}
virtual void showStatus()
{
AddWeaponEffect();
std::cout << "血量:" << mHp << std::endl;
std::cout << "魔法:" << mMp << std::endl;
std::cout << "攻击:" << mAt << std::endl;
std::cout << "防御:" << mDf << std::endl;
std::cout << "武器装饰器调用" << std::endl;
pHero->showStatus();
}
};
void test01()
{
AbstractHero* hero = new HeroA;
hero->showStatus();
std::cout << "-------------------" << std::endl;
// 给英雄穿上铠甲
hero = new ArmorEquipment(hero);
hero->showStatus();
std::cout << "-------------------" << std::endl;
// 给英雄装备武器
hero = new WeaponEquipment(hero);
hero->showStatus();
std::cout << "-------------------" << std::endl;
hero->showStatus();
}
int main()
{
test01();
return 0;
}
血量:0
魔法:0
攻击:0
防御:0
抽象装饰器,被装饰对象地址:0x620e70
英雄穿上铠甲之后
血量:0
魔法:0
攻击:0
防御:30
铠甲装饰器调用
血量:0
魔法:0
攻击:0
防御:0
抽象装饰器,被装饰对象地址:0x621ea0
英雄装备武器之后
血量:0
魔法:0
攻击:50
防御:30
武器装饰器调用
英雄穿上铠甲之后
血量:0
魔法:0
攻击:0
防御:30
铠甲装饰器调用
血量:0
魔法:0
攻击:0
防御:0
英雄装备武器之后
血量:0
魔法:0
攻击:50
防御:30
武器装饰器调用
英雄穿上铠甲之后
血量:0
魔法:0
攻击:0
防御:30
铠甲装饰器调用
血量:0
魔法:0
攻击:0
防御:0
具体怎么理解呢,就是一开始有一个具体英雄A对象,但是我想增加一个新的方法,但是如果直接改动A对象不是很好
,而且以后如果还想增加新的方法,那岂不是会经常继承然后重写showStatus呢。
所以我就使用一个同样继承抽象英雄类的抽象装饰器类,后续如果想增加就继承这个抽象装饰器类就行。
得到具体装饰器类后就可以写新的方法了,放在虚函数showStatus里面就行。由于创建具体装饰器指针的时候里面也会创建个AbstractHero指针,所以如果传入一个具体英雄对象(或者其他装饰器指针)也能接受数据,所以新增加的方法都是对这个类里面的新指针进行操作。
每次使用装饰器都会产生新的指针。按照案例给的代码,这里就会有三个指针,分别指向HeroA、ArmorEquipment、WeaponEquipment。如果想获得第三个指针的数据,那么第一个、第二个指针都不能删除,因为数据都是根据这些指针算出来的,并且在指针内部都有一个指针指向前面的指针的。
感觉跟混入的概念有点像。