装饰模式
定义
装饰者模式(Decorator Pattern)
装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
简单概括就是:对于一个类,继承他并持有他,然后拓展他的功能
UML
我们有一个Component类及其实现ConcreteComponent
现在做一个装饰类Decorator,并实现装饰器A和装饰器B,通过两个装饰器,我们可以拓展Component及其自类的功能
代码
Componnet及其实现
namespace DesignModel.Decorator
{
//抽象类Component
public abstract class Component
{
public abstract void Operate();
}
//其实现ConcreteComponent
public class ConcreteComponent : Component
{
public override void Operate()
{
Debug.Log("Concrete Component");
}
}
}
装饰器Decorator,实现A,实现B
namespace DesignModel.Decorator
{
//抽象装饰器,继承自Component
public abstract class Decorator : Component
{
//持有Component对象
protected Component item;
//初始化时添加Component对象,或另外实现
public Decorator(Component component)
{
item = component;
}
public virtual void SetComponent(Component component)
{
item = component;
}
public override void Operate()
{
if (item != null)
{
item.Operate();
}
}
}
//装饰器A
public class ConcreteDecoratorA : Decorator
{
//装饰一个新的状态
private string addState;
public ConcreteDecoratorA(Component component):base(component){}
public override void Operate()
{
base.Operate();
addState = "new State";
Debug.Log("Concrete Decorator A :" + addState);
}
}
//装饰器B
public class ConcreteDecoratorB : Decorator
{
public ConcreteDecoratorB(Component component):base(component){}
public override void Operate()
{
base.Operate();
AddBehavior();
}
//添加一个新的方法
private void AddBehavior()
{
Debug.Log("ConcreteDecoratorB : new Behavior ");
}
}
}
简单调用
public class Client_Decorator : MonoBehaviour
{
void Start ()
{
DesignModel.Decorator.Component component = new ConcreteComponent();
DesignModel.Decorator.Component decoratorA = new ConcreteDecoratorA(component); //可以装饰属性
DesignModel.Decorator.Component decoratorB = new ConcreteDecoratorB(component); //可以装饰方法
decoratorA.Operate();
decoratorB.Operate();
Debug.Log("_____ 叠加 ______");
component = decoratorB;
((ConcreteDecoratorB)component).SetComponent(decoratorA); //可以叠加装饰 但是出错后很难查证
component.Operate();
}
}
结果
拓展
从类继承(泛化)与类组合来看
设计模式的原则是优先使用组合
桥接模式和装饰模式都是对类进行组合,减少类的数量
适配器模式则是对类进行泛化
适配器模式是对目标接口进行继承, 并调用原接口去实现,目的是为了将一个接口转化为另一个接口
装饰模式是对原接口进行继承,并用原接口的对象进行实现,目的是为了拓展原接口对象的功能
从持有对象来看
适配器模式持有原接口的具体对象
装饰模式持有原接口的具体对象
桥接模式持有另一个抽象接口的抽象对象
从组合型设计模式的目的来看
桥接模式 和 装饰模式 都是使用组合解决子类过多的问题, 但他们的诱因不同:
1.桥接模式 对象自身有 沿着多个维度变化的趋势 , 本身不稳定;
2.装饰模式 对象自身非常稳定, 只是为了增加新功能/增强原功能。
一个同桥接模式类似的例子
装饰模式下
有 人类(Human)、精灵(Elve) 他们继承自Person
有装饰器(Decorator),继承自Person,并持有一个Person
有武器 剑(Sword),枪(Gun)继承自Decorator
有技能(Skill)继承自Decorator
这样,我们可以给Person装饰上武器和技能
UML
代码
抽象的Person及其实现Human
namespace DesignModel.Decorator
{
public abstract class Person
{
public abstract void Attack();
}
public class Human : Person
{
public override void Attack()
{
Debug.Log("Human Attack !");
}
}
}
抽象装饰器及实现
namespace DesignModel.Decorator
{
//装饰器
public abstract class PersonDecorator : Person
{
protected Person person;
public PersonDecorator(Person p)
{
person = p;
}
public override void Attack()
{
if (person != null)
person.Attack();
}
}
//武器:剑
public class Sword : PersonDecorator
{
public Sword(Person p):base(p){}
public override void Attack()
{
base.Attack();
Brandish();
}
private void Brandish()
{
Debug.Log(" Brandish Sword !");
}
}
//技能
public class Skill : PersonDecorator
{
public Skill(Person p):base(p) {}
public override void Attack()
{
base.Attack();
Release();
}
private void Release()
{
Debug.Log(" Release Skill !");
}
}
}
简单测试
public class Client_Decorator_2 : MonoBehaviour
{
void Start ()
{
Person human = new Human();
human.Attack();
Sword human_sword = new Sword(human);
human_sword.Attack();
Skill human_sword_skill = new Skill(human_sword);
human_sword_skill.Attack();
}
}
结果
桥接模式下
有 人类(Human)、精灵(Elve) 他们继承自Person
有武器 剑(Sword),枪(Gun)继承自 Weapon
有技能(Skills)继承自Skill
Person抽象类持有 Weapon 和 Skill 的抽象类对象, 以此为桥,保证Person、Weapon、Skill都可以独立变化
代码及UML见桥接模式
对比二者
相同:
都实现了对Person、武器和技能的组合
不同:
抽象层面: 装饰模式只有Person的抽象,武器和技能都是装饰,而非独立对象
桥接模式中Person、Weapon、Skill都是抽象,Person持有Weapon和Skill的独立对象
装饰模式中实现的是不同功能的子类进行封闭后独立的子类,但仍旧是紧耦合(因为是继承方式),而桥接模式中是将变化独立开,降低类之间的耦合度,尽最大可能实现松耦合(组合方式)
装饰模式是动态地添加一些额外功能的模式,也就是说装饰模式是适应新需求而添加新功能,并且不影响其他对象的一种模式;而桥接模式是适应变化维度的一种模式,它在于将对象的各个维度的变化都独立开来,使一些变化不受其他因素变化的影响。
优缺点:
相比桥接模式,装饰模式更灵活,可以动态无限叠加装饰(比如给剑上打宝石,再给宝石附魔等等)或撤销,同时也可以定义不同类型的装饰
而桥接模式在设计层面分离了Person和Weapon,使二者可以独立变化,解藕合,并不易出错
装饰模式的适用性:
1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2)处理那些可以撤消的职责。
3)当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
桥接模式的适用性:
1).如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
2).设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。
3).一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合。
4).虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
对于我们来说:
Person和Weapon/Skill,是很明显的独立维度变化,因此应该使用桥接模式
而我们要给Skill附加眩晕效果,则可以使用装饰模式(眩晕并不是一个独立变化的维度,而不同的技能都可能附带眩晕效果,同时,技能也可能附带多个效果)