装 饰 器 模 式
“装饰还是武器,都由我来定义。”
——柯莱塔·莫塔里
只是突然想起台词,并非魔怔!
正如同漂泊者被赠与的猫眼石一般,装饰品是独立于本体之外的事物。装饰器模式(Decorator)允许向一个现有的对象添加新的功能,同时又不改变其结构,只是作为附加物存在。
使用场景:
装饰器模式是一种结构型模式,常常用在需要运行时动态添加或修改类的功能时。就比如我们玩游戏时常遇到的“buff”。作为游戏角色的附加效果,完全可以通过装饰器模式实现。
实现:
装饰器模式包含以下四种角色:
1、抽象组件,按照设计模式大家族的传统,抽象就代表接口的意思。而这里的组件,便是我们的装饰器所要装饰的对象。
2、具体组件,组件的具体实现。
3、抽象装饰器,装饰器的基类,统一规范,而这一装饰器基类必须继承自抽象组件。
4、具体装饰器,装饰器的具体实现。
那么为什么必须用继承来连接装饰器和被装饰对象呢?
我们已经明确了装饰器在不改变修饰对象内容的前提下为其附加效果,那么这样一来装饰器必须传入两样东西:要装饰的对象和要添加的效果,最终输出的是已经带有效果的对象。当我们直接使用原来的对象时,会将原本的内容直接输出,而当使用适配器时,我们为了输出带有效果的对象,我们需要一个原来对象的实例,通过组合连接,为了让得到输出的结果一致,再通过继承规范实现。所以我们的抽象器既要组合组件,也要继承于组件基类。
只靠讲的还是太啰嗦了!上代码!
那么以下是C++实现。
#include <iostream>
using namespace std;
#include <memory>
/*抽象组件*/
//游戏角色接口
class Character {
public:
virtual int getAttack() = 0; //获取攻击力
virtual int getDefense() = 0; //获取防御力
virtual ~Character() = default;
};
/*具体组件*/
class Goblin : public Character {
public:
int getAttack() override {
return 10; //哥布林的攻击力}
int getDefense() override {
return 5; //哥布林的防御力}
};
class Slime : public Character {
public:
int getAttack() override {
return 5; //史莱姆的攻击力}
int getDefense() override {
return 2; //史莱姆的防御力}
};
/*抽象装饰器*/
//继承自游戏角色接口,这样一来,规范与角色一致,使用时装饰器就可以被用来装饰任何游戏角色
class CharacterDecorator : public Character {
public:
//这个成员代码被装饰的角色,作用就是“被装饰”!Character* character;CharacterDecorator(Character* character) : character(character) {}
//基类将它们委托给被装饰的角色,默认情况下,装饰器不改变角色的行为,
int getAttack() override {
return character->getAttack();}
int getDefense() override {
return character->getDefense();}
virtual ~CharacterDecorator() {delete character;}
};
/*具体装饰器*/
class ArmorDecorator : public CharacterDecorator {
public:ArmorDecorator(Character* character) : CharacterDecorator(character) {}
int getAttack() override {
return character->getAttack();}
int getDefense() override {
return character->getDefense() + 5; //防御力buff加成5}
};
class WeaponDecorator : public CharacterDecorator {
public:WeaponDecorator(Character* character) : CharacterDecorator(character) {}
int getAttack() override {
return character->getAttack() + 5; //攻击力buff加成5}
int getDefense() override {
return character->getDefense();}
};
int main() {
//创建一个哥布林角色Character* goblin = new Goblin();cout << "哥布林攻击力: " << goblin->getAttack() << endl;cout << "哥布林防御力: " << goblin->getDefense() << endl;
//给哥布林添加护甲buff装饰器goblin = new ArmorDecorator(goblin);cout << "哥布林攻击力: " << goblin->getAttack() << endl;cout << "哥布林防御力: " << goblin->getDefense() << endl;
//给哥布林添加攻击buff装饰器goblin = new WeaponDecorator(goblin);cout << "哥布林攻击力: " << goblin->getAttack() << endl;cout << "哥布林防御力: " << goblin->getDefense() << endl;
//注意:这里内存没有释放干净,会引发内存泄露,建议使用智能执指针!
//将Character*改为std::shared_ptr<Character>,装饰器中使用std::make_shared来创建对象
//此处只作为参考delete goblin; //释放内存
return 0;
}
这样看来,装饰器继承于角色类,其输出为带buff的角色,可以直接代替原来的角色。通过这一点,我们可以做到buff叠加,也就是嵌套装饰器。
小结
装饰器模式就到此为止了,在游戏开发中,不仅是角色的buff,还有环境效果、特殊增益,甚至NPC的行为逻辑,都有装饰器模式的用武之地。
如有补充交流欢迎留言,我们下次再见~
参考列表:
菜鸟教程-设计模式:www.runoob.com