02.09 装饰模式

设计模式——装饰模式

需求

从一个编程任务说起。有段旧代码如下:

        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();

        }

    }

优缺点

优点:适用装饰模式,能够比使用继承关系更灵活的扩展对象的功能,它可以动态地增加对象的功能,并且可以随意组合这些功能。

缺点:正是因为可以由客户端随意组合功能,就可能被客户组合出一些不合理的逻辑,就像上面案例中既穿裙子,又穿长裤的荒唐逻辑。

适用场景

在不修改原类的情况下,需要动态地给一个对象增加一些额外的功能,并且这些额外的功能还需要可以随意组合,就非常适合装饰模式了。可以说装饰模式解决了动态扩展对象功能的问题。

补充

原文地址:https://www.cnblogs.com/sagahu/p/2714230.html