装饰模式(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("\n第一种装扮");
 5             xmw.WearTshirts();
 6             xmw.WearBigTrouser();
 7             xmw.WearSneakers();
 8             xmw.Show();
 9             Console.WriteLine("\n第二种装扮");
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("\n第一种装扮");
 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("\n第二种装扮");
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,装饰模式讲完了,一句话总结一下:装饰模式是为已有功能动态添加更多功能的一种方式

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


 

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

posted @ 2017-01-09 10:06  萌萌丶小魔王  阅读(682)  评论(1编辑  收藏  举报