个人理解:装饰模式的内核是叠加,即在某个功能的基础上,增加这个功能的行为,使得这个功能能够实现更多的事情。
装饰模式定义:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
从定义我们可以看出:装饰模式的作用类似于继承,我们子类继承自某个父类,也能达到扩展父类功能的效果,但是就像它说的,不够灵活,装饰模式给我们提供了一种能在代码中动态添加功能的方法。这样的优点尤其在某些职责复用次数特别多,但是又不是每个类都要用到的时候,优势特别明显,因为如果我们要创建能实现所有功能的子类,那就需要排列组合n次,会创建出特别多冗余的类,而使用装饰模式不会。
装饰的过程:类似于unity组件式开发的思路,一个空的对象(可能只带有transform位置信息),给它加上text组件,它就具有text的职责,然后还可以继续在已经添加了text组件的对象上继续添加描边、阴影等组件。
1️⃣ 要实现装饰模式,首先需要保证各个组件都继承自同一个基类,且最终调用同一个方法,这里定义虚基类用作组件继承。
abstract class Component
{
public abstract void Operation();
//抽象函数,子类一定要实现
}
2️⃣ 接下来可以定义可以被装饰的空对象,这个对象也可以实现Operation,具有自己的行为:
class ConcreteObject : Component
{
public override void Operation()
{
Console.WriteLine("可以自定义的操作");
}
}
3️⃣ 下一步定义装饰对象的功能基类,这也是装饰模式的根本。
class ConcreteComponent : Component
{
private Component com;
public void SetComponent(Component com)
{
this.com = com;
}
public override void Operation()
{
if(this.com != null)
{
this.com.Operation();
}
}
}
装饰基类的Operation,需要具有执行被装饰对象Operation的功能。
4️⃣ 定义两个具体功能类:
class Outline : ConcreteComponent
{
public override void Operation()
{
base.Operation(); // 先执行原组件的Operation
Console.WriteLine("添加了描边组件");
}
}
class Shadow : ConcreteComponent
{
public override void Operation()
{
base.Operation(); // 先执行原组件的Operation
Console.WriteLine("添加了阴影组件");
}
}
被装饰后的对象,如果执行Operation,先执行被他装饰的对象的Operation。
5️⃣ 客户端代码如下:
static void Main(String[] args)
{
//先定义被装饰的对象
Component obj = new ConcreteObject();
//定义装饰功能对象
Component outline = new Outline();
Component shadow = new Shadow();
//装饰过程
outline.SetComponent(obj);
//将Outline装饰到obj上,outline就是被装饰后的对象
shadow.SetComponent(outline);
//对上一层装饰后的对象进行二次装饰,shadow就是二次装饰后的对象
//最后应该调用最后一次装饰得到的对象的Operation
shadow.Operation();
}
有一个游戏角色,我们需要给他装备上各种装备,比如说武器,铠甲,足具等等,每种装备都可以提升属性,也可以改变人物装饰。这时候可以使用装饰模式,将获取人物各种属性的函数以及获取人物装扮的函数抽象出来,在具体的装备装饰器中重写这些函数,获取时做一些数值或文本修改:
int override GetHp() { return this.character.GetHp() + 50; }
上面的例子都是写一个单独的函数进行装饰,这里可以考虑在构造函数里面进行装饰:
void Kaijia(Character cha) { this.character = cha; }