设计模式-装饰器模式

简介

装饰器(又称修饰器)模式是设计模式之一,它能在不改变原有类的情况下扩展已有类的功能。
在游戏开发中,装饰器模式适用于装扮或者功能性道具的交互,这样角色类本身就不需要包含对应逻辑。

例子

简单地,装饰类可以写成一个组合目标对象,但不包含目标本身原有数据的类。

例如有一个角色类Character,拥有血量(HP)属性,可以实现移动(Move),进食(Eat)等基础功能。现在想添加两种药水道具,使角色吃下去后分别可以飞行、游泳。

public class Decorator:Character
{
    protected Character player;
    public new int HP { get { return player.HP; } }
    public new void Move() { player.Move(); }
    public new void Eat() { player.Eat(); }
}
public class FlyDecorator:Decorator
{
    public FlyDecorator(Character _player)
    {
        player = _player;
    }
    public void Fly() { Debug.Log("Fly"); }
}
public class SwimDecorator:Decorator
{
    public SwimDecorator(Character _player)
    {
        player = _player;
    }
    public void Swim() { Debug.Log("Swim"); }
}
Character Acoloco = new Character(); 
FlyDecorator FlyAcoloco = new FlyDecorator(Acoloco); 
SwimDecorator SwimAcoloco = new SwimDecorator(FlyAcoloco); 
Acoloco.Move();
FlyAcoloco.Fly(); 
SwimAcoloco.Swim();

可以看到,使用装饰器模式扩展了原有Character类的功能。这在不方便修改原类的时候尤为有效,例如当你使用了某些框架时,你可以结合具体业务需求编写修饰类,扩展类功能从而简化工作。

更多

为什么修饰类要继承原有基类,不继承会怎么样?

public class Decorator    //不再继承Character
{
    protected Character player;
    //不需要再使用new隐藏
    public int HP { get { return player.HP; } }
    public void Move() { player.Move(); }
    public void Eat() { player.Eat(); }
}

FlyCharacterSwimCharacter部分保持不变

在取消继承基类的情况下,上面的程序仍可正常运行,但是此时有以下缺点:

  1. Decorator与Character类型不兼容。失去继承关系后,任何需要填入Character类型参数的地方,都不能使用Decorator替代,极大地限制了Decorator的使用范围。
  2. 由于第一点,被修饰过实例的二次修饰受到限制。例如原先A,B,C三个修饰类可以互相修饰,但在失去共同基类后,只能采用B修饰A,C修饰B这样的顺序修饰(除非你愿意把所有修饰情况全写进去)。

为什么不直接使用继承(使用子类),而是使用组合(使用装饰器)?

public class Decorator:Character
{

}
public class FlyDecorator:Decorator
{
    public void Fly() { Debug.Log("Fly"); }
}
  • 装饰器模式使用组合而非继承,减少了可能存在的子类的数量。例如某个角色类有6种形态和7套装扮(假设形态和装扮系统只在某个特殊游戏模式中生效,那将其直接组合至角色类中显然是不合适的,因此需要使用装饰器)。直接使用继承的话需要编写6*7=42个类,而使用组合只需要编写6+7=13个类,显然是使用组合更加高效。
  • 使用组合可以动态增减功能。例如对于Character类型的实例Acoloco,当要增加飞行功能时可以直接new FlyCharacter(Acoloco)得到会飞的FlyCharacter类型实例,而要减少功能时可以直接停止使用FlyCharacter类,继续使用Character类。若使用继承,则需要将实例在不同的类型间转换,并在每次转换类型的同时都要完成数据的拷贝。
  • 使用组合能减少数据存储,利于维护。FlyCharacter类型本身只引用Character中的数据类型,而不储存。而使用继承(使用子类)就需要先利用Character类中数据对FlyCharacter类型数据进行初始化,此时同时存在多份数据,不易管理。
© 版权声明
THE END
喜欢就点个赞吧
点赞1 分享
评论 共1条

请登录后发表评论

    暂无评论内容