设计模式——装饰模式
需求
从一个编程任务说起。有段旧代码如下:
public class Girl
{
public virtual void GoSchool() { Console.Write("女孩去上学!"); }
}
客户提出了新的要求,随着时代的进步,现代女孩上学还需要增加新的功能,有带个花花帽子,穿个漂亮裙子,画眉涂口红等可选功能,但是旧类不允许修改,因为旧的功能还是适合那些传统女孩,还在那些情况下有用。总结就是:不修改旧类代码的情况下,需要增加新的功能;这些新增的功能还可以任意组合。下面先尝试用继承机制来实现。
继承旧类来实现添加功能:女孩戴个花花帽子
public class GirlCap : Girl
{
public override void GoSchool()
{
Console.WriteLine("女孩戴个花花帽子;");
base.GoSchool();
}
}
继承旧类来实现添加功能:女孩穿个漂亮裙子
public class GirlSkirt : Girl
{
public override void GoSchool()
{
Console.WriteLine("女孩穿个漂亮裙子;");
base.GoSchool();
}
}
前面两个单独的功能都实现了,戴帽子与穿裙子的组合功能也是可以实现的,
public class GirlCapSkirt : GirlSkirt
{
public override void GoSchool()
{
Console.WriteLine("女孩戴个花花帽子;");
base.GoSchool();
}
}
但是到这里我们可以发现,如果还有其它的单独功能可以用单独的子类来实现,但是他们的各种组合就比较麻烦。更糟糕的是,随着各项需要新增功能的增多,还需要增加很多单独功能子类及所有可能组合功能的子类。就是说这种需求使用继承来解决将会陷入非常复杂的情况。装饰模式(Decorator Pattern)就是解决这种问题的。
定义
装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。若要扩展功能,装饰提供了比继承更具弹性的代替方案。
意图:动态地给一个对象添加一些额外的职责。
实现方法:装饰模式使用被装饰类的一个子类的实例,在客户端将这个子类的实例委托给装饰类来,从而获得额外的功能。
装饰模式由4部分组成:(1)抽象的被装饰者(Abstract Component),定义被装饰着规格;(2)具体的被装饰者(Concrete Component),实现抽象被装饰者;(3)抽象的装饰类(Decorator),引用被装饰者一个子类的实例,原搬被装饰者的功能,为各子类添加额外功能提供基类;(4)具体的装饰类(Concrete Decorator),继承/实现抽象的装饰类,在重定义方法里增加一项单独的装饰功能。
案例
class Program
{
// 抽象的装饰目标
public interface IGirl { void GoSchool();}
// 具体的装饰目标
public class Girl : IGirl
{
public virtual void GoSchool() { Console.WriteLine("女孩去上学!"); }
}
// 抽象装饰类
public class GirlDecorator : IGirl
{
public GirlDecorator(IGirl girl) { this._Girl = girl; }
private IGirl _Girl; // 内部引用一个需要装饰的兑现实例
public virtual void GoSchool() { this._Girl.GoSchool(); }
}
// 具体装饰类:女孩戴个花花帽子
public class GirlCapDecorator : GirlDecorator
{
public GirlCapDecorator(IGirl girl) : base(girl) { }
public override void GoSchool()
{
Console.WriteLine("女孩戴个花花帽子;");
base.GoSchool();
}
}
// 具体装饰类:女孩穿个漂亮裙子
public class GirlSkirtDecorator : GirlDecorator
{
public GirlSkirtDecorator(IGirl girl) : base(girl) { }
public override void GoSchool()
{
Console.WriteLine("女孩穿个漂亮裙子;");
base.GoSchool();
}
}
// 具体装饰类:女孩穿个漂亮长裤
public class GirlTrousersDecorator : GirlDecorator
{
public GirlTrousersDecorator(IGirl girl) : base(girl) { }
public override void GoSchool()
{
Console.WriteLine("女孩穿个漂亮长裤;");
base.GoSchool();
}
}
static voidMain(string[] args)
{
// 客户程序
// 女孩戴个花花帽子,穿着漂亮裙子去上学:
IGirl girl1 = new GirlSkirtDecorator(new GirlCapDecorator(new Girl()));
girl1.GoSchool();
// 女孩戴个花花帽子,穿着漂亮长裤去上学:
IGirl girl2 = new GirlTrousersDecorator(new GirlCapDecorator(new Girl()));
girl2.GoSchool();
// 女孩穿着漂亮裙子,穿着漂亮长裤去上学:这个逻辑比较荒唐,说明了装饰模式的缺点
IGirl girl3 = new GirlSkirtDecorator(new GirlTrousersDecorator(new Girl()));
girl3.GoSchool();
}
}
优缺点
优点:适用装饰模式,能够比使用继承关系更灵活的扩展对象的功能,它可以动态地增加对象的功能,并且可以随意组合这些功能。
缺点:正是因为可以由客户端随意组合功能,就可能被客户组合出一些不合理的逻辑,就像上面案例中既穿裙子,又穿长裤的荒唐逻辑。
适用场景
在不修改原类的情况下,需要动态地给一个对象增加一些额外的功能,并且这些额外的功能还需要可以随意组合,就非常适合装饰模式了。可以说装饰模式解决了动态扩展对象功能的问题。
补充