• 装饰模式(5)


    本篇博文,给大家讲解一下装饰模式,还是老样子,有一个简单的例子逐步演绎

    一、举例

    用一个简单的控制台实现 一个人穿各种各样衣服 的功能

    然后我们会很自然的写出一下代码:

    先写一个Person类

     1  class Person
     2     {
     3         private string name;
     4         public Person(string name)
     5         {
     6             this.name = name;
     7         }
     8         public void WearTshirts()
     9         {
    10             Console.WriteLine("大T恤");
    11         }
    12         public void WearBigTrouser()
    13         {
    14             Console.WriteLine("跨裤");
    15         }
    16         public void WearSneakers()
    17         {
    18             Console.WriteLine("破球鞋");
    19         }
    20         public void WearSuit()
    21         {
    22             Console.WriteLine("西装");
    23         }
    24         public void WearTie()
    25         {
    26             Console.WriteLine("领带");
    27         }
    28         public void WearLeatherShoes()
    29         {
    30             Console.WriteLine("皮鞋");
    31         }
    32         public void Show()
    33         {
    34             Console.WriteLine($"{name}装扮完毕!");
    35         }
    36     }

    然后客户端调用这个Person类

     1  static void Main(string[] args)
     2         {
     3             Person xmw = new Person("小魔王");
     4             Console.WriteLine("
    第一种装扮");
     5             xmw.WearTshirts();
     6             xmw.WearBigTrouser();
     7             xmw.WearSneakers();
     8             xmw.Show();
     9             Console.WriteLine("
    第二种装扮");
    10             xmw.WearSuit();
    11             xmw.WearTie();
    12             xmw.WearLeatherShoes();
    13             xmw.Show();
    14             Console.ReadKey();
    15         }

    这样就写完了。

    二、演绎

    ①现在,我各种装扮都写到了Person类中,有这样一个问题,假如,有一天,我新加了一件衣服,比如"皮裤"哈哈,那么,我该怎么办呢?

    很多小伙伴就说了,这很容易啊,直接在Person类中增加一个“皮裤”的方法不就完事了吗。

    这的确可以解决这个问题,这也是大多数人解决这个问题的方法。但是,真正的高手可以看出里面的弊端。

    首先,给大家介绍程序设计中的一个重要原则——开放-封闭原则,这个原则讲的是:软件实体(类,模块,函数等等)应该可以扩展,但是不可修改。也就是说对于扩展是开放的,对于修改是封闭的。

    那么,用 修改Person类来解决上述的问题就违背了开放-封闭原则。

    那我们该如何解决上述这个问题呢?

    于是,我们想到这样一个设计方法:

     1  /// <summary>
     2     /// Person类
     3     /// </summary>
     4     class Person
     5     {
     6         private string name;
     7         public Person(string name)
     8         {
     9             this.name = name;
    10         }
    11         public void Show()
    12         {
    13             Console.WriteLine($"{name}装扮完毕");
    14         }
    15     }
    16     /// <summary>
    17     /// 服饰抽象类
    18     /// </summary>
    19     abstract class Finery
    20     {
    21         public abstract void Show();
    22     }
    23     //各种服饰子类
    24     class TShirts : Finery
    25     {
    26         public override void Show()
    27         {
    28             Console.WriteLine("大T恤");
    29         }
    30     }
    31     class BigTrouser : Finery
    32     {
    33         public override void Show()
    34         {
    35             Console.WriteLine("跨裤");
    36         }
    37     }
    38     class Sneakers : Finery
    39     {
    40         public override void Show()
    41         {
    42             Console.WriteLine("破球鞋");
    43         }
    44     }
    45     class Surt : Finery
    46     {
    47         public override void Show()
    48         {
    49             Console.WriteLine("西装");
    50         }
    51     }
    52     class Tie : Finery
    53     {
    54         public override void Show()
    55         {
    56             Console.WriteLine("领带");
    57         }
    58     }
    59     class LeatherShoes : Finery
    60     {
    61         public override void Show()
    62         {
    63             Console.WriteLine("皮鞋");
    64         }
    65     }

    上述,我们把各种各样的服饰写成单个的类。

    客户端:

     1  static void Main(string[] args)
     2         {
     3             Person xmw = new Person("小魔王");
     4             Console.WriteLine("
    第一种装扮");
     5             Finery dtx = new TShirts();
     6             Finery kk = new BigTrouser();
     7             Finery pqx = new Sneakers();
     8             dtx.Show();
     9             kk.Show();
    10             pqx.Show();
    11             xmw.Show();
    12             Console.WriteLine("
    第二种装扮");
    13             Finery xz = new Surt();
    14             Finery ld = new Tie();
    15             Finery px = new LeatherShoes();
    16             xz.Show();
    17             ld.Show();
    18             px.Show();
    19             xmw.Show();
    20             Console.ReadKey();
    21         }

    这样的话,如果要增加新的服饰,我只需要增加一个Finery 的子类就可以了。

    在这里,我们用到了继承,用到了抽象,也做到了‘服饰’类与‘人’类的分离,的确进步不少,但还存在一些其他的问题。

    什么问题呢?我们来看一下这段代码:

    1             dtx.Show();
    2             kk.Show();
    3             pqx.Show();
    4             xmw.Show();

    怎么看怎么别扭呢。在客户端把穿的各种服饰一个一个的显示出来。这就好比,在客户面前,光着身子把各种服饰一件一件的穿上一样。这岂不是太丢人?我总不能当着客户的面一件一件的穿衣服吧,我应该在别的地方一件一件的穿好了衣服,然后再去见客户。

    所以,针对上述问题,我们应该在内部组装完毕之后,在整体展现出来。(类似于建造者模式,但有所不同,最大的区别在于,建造者模式中的建造过程是稳定的,而我们这个例子中穿各种服饰的过程是不稳定的。在之后的博文中我会讲到建造者模式。)

    我们可以想象一下穿衣服打扮的场景过程:我们从一大堆服饰中挑选自己习惯的来穿上,但是,穿衣服也是有顺序的,比如,先穿内裤,再穿毛裤。。。。但是,你可以玩个性的,先穿毛裤,再穿内裤。。。额,想想下面就刺挠。。

    如果我们想要实现上述场景的功能,该如何设计呢? 这貌似很难哦。

    不要怕,接下来我们要讲的装饰模式可以很好的解决上述问题。

     首先,我们来看一下装饰模式的基本构成和作用。

     1     /// <summary>
     2     /// 此接口可以对这些对象动态的添加职责
     3     /// </summary>
     4     abstract class Component
     5     {
     6         public abstract void Operation();
     7     }
     8     /// <summary>
     9     /// 具体的对象,可以给这个对象添加一些职责
    10     /// </summary>
    11     class ConcreteComponent : Component
    12     {
    13         public override void Operation()
    14         {
    15             Console.WriteLine("具体对象的操作");
    16         }
    17     }
    18     /// <summary>
    19     /// 装饰类,继承Component,目的是从外类来扩展继承Component类的功能
    20     /// 对于Component来说,无须知道Decorator的存在。
    21     /// </summary>
    22     abstract class Decorator : Component
    23     {
    24         protected Component component;
    25         //设置Componnet
    26         public void SetComponent(Component component)
    27         {
    28             this.component = component;
    29         }
    30         //重写Operation(),实际执行的是Component的Operation()
    31         public override void Operation()
    32         {
    33             if (component != null)
    34             {
    35                 component.Operation();
    36             }
    37         }
    38     }
    39     /// <summary>
    40     /// 具体的装饰对象,起到给Component添加功能的作用。
    41     /// </summary>
    42     class ConcreteDecoratorA : Decorator
    43     {
    44         private string addedState;//本类独有的方法,以区别与ConcreteDecoratorB
    45         public override void Operation()
    46         {
    47             //首先运行原Component的Operation(),再执行本类的功能addedSate,相当于对原Component进行了装饰
    48             base.Operation();
    49             addedState = "New State";
    50             Console.WriteLine("具体装饰对象A的操作");
    51         }
    52     }
    53     class ConcreteDecoratorB : Decorator
    54     {
    55         public override void Operation()
    56         {
    57             //首先运行原Component的Operation(),再执行本类的功能 AddedBehavior(),相当于对原Component进行了装饰
    58             base.Operation();
    59             AddedBehavior();
    60             Console.WriteLine("具体装饰对象B的操作");
    61         }
    62 
    63         private void AddedBehavior()
    64         {
    65             //本类独有的方法,以区别与ConcreteDecoratorA
    66         }
    67     }

    根据功能的扩展需要,可能还有ConcreteDecoratorC,ConcreteDecoratorD,等等这样的添加功能作用的类。

    上述代码就是装饰模式的基本形式。一定要理解哦,注释已经很明确了哦。

     客户端:

     1   static void Main(string[] args)
     2         {
     3             ConcreteComponent c = new ConcreteComponent();
     4             ConcreteDecoratorA d1 = new ConcreteDecoratorA();
     5             ConcreteDecoratorB d2 = new ConcreteDecoratorB();
     6             //装饰的方法是:
     7             //首先用ConcreteComponent实例化对象c,
     8             //然后用ConcreteDecoratorA的实例化对象d1来包装c,
     9             //再用ConcreteDecoratorB的对象d2包装d1.最终执行d2的Operation()
    10             d1.SetComponent(c);
    11             d2.SetComponent(d1);
    12             d2.Operation();
    13             Console.ReadKey();
    14         }

     下面,我们将装饰模式运用到一开始举的案例中。

     1    class Person
     2     {
     3         //构造函数
     4         public Person()
     5         {
     6 
     7         }
     8         private string name;
     9         //构造函数
    10         public Person(string name)
    11         {
    12             this.name = name;
    13         }
    14         public virtual void Show()
    15         {
    16             Console.WriteLine($"{name}装扮完毕");
    17         }
    18     }
    19 
    20     class Finery : Person
    21     {
    22         protected Person component;
    23         //设置Component
    24         public void Decorate(Person component)
    25         {
    26             this.component = component;
    27         }
    28         //重写Show()方法,实际执行的是Component中的Show()
    29         public override void Show()
    30         {
    31             if (component!=null)
    32             {
    33                 component.Show();
    34             }
    35         }
    36     }
    37 
    38     class TShirts:Finery
    39     {
    40        
    41         public override void Show()
    42         {
    43             //先执行本类中的功能
    44             Console.WriteLine("大T恤");
    45             //在执行 Component 中的Show();
    46             base.Show();
    47 
    48             //相当于对原Component进行装饰
    49         }
    50     }
    51     class BigTrouser:Finery
    52     {
    53         //同TShirts类
    54 
    55         public override void Show()
    56         {
    57             Console.WriteLine("垮裤");
    58             base.Show();
    59         }
    60     }
    61     
    62     //***************其余类似装扮,省略**********

    相比装饰模式模板的代码,我们少了两个抽象类,这里我么你要学会变通,一些不必要的类,我们可以省略,思路对即可。没有必要照葫芦画瓢,只有对设计模式达到灵活运用的时候,我们才能接近高手。

    客户端调用

     1   static void Main(string[] args)
     2         {
     3             Person xmw = new Person("小魔王");
     4             Console.WriteLine("第一种装扮");
     5             BigTrouser bg = new BigTrouser();
     6             TShirts ts = new TShirts();
     7             bg.Decorate(xmw);
     8             ts.Decorate(bg);
     9             ts.Show();
    10             Console.ReadKey();
    11         }

    ok,装饰模式讲完了,一句话总结一下:装饰模式是为已有功能动态添加更多功能的一种方式

    好了,今天就到这吧,下一篇,会讲 代理模式


    本系列将持续更新,喜欢的小伙伴可以点一下关注和推荐,谢谢大家的支持。

  • 相关阅读:
    「SDOI2018」物理实验
    「SDOI 2018」战略游戏
    「CodeChef Dec13 REALSET」 Petya and Sequence 循环卷积
    关于微信卡券投放code接口报错原因
    composer update maatwebsite/excel 之后 在linux机子上出现500解决方案
    开启mysql 服务【window】
    thinkphp在linux上部署环境(500情况)
    如何推广微信小程序到企业微信
    linux 阿里云远程连接mysql
    php7以上 不支持mcrypt_module_open方法问题【微信开放平台】
  • 原文地址:https://www.cnblogs.com/xiaomowang/p/6249040.html
Copyright © 2020-2023  润新知