本篇博文,给大家讲解一下装饰模式,还是老样子,有一个简单的例子逐步演绎
一、举例
用一个简单的控制台实现 一个人穿各种各样衣服 的功能
然后我们会很自然的写出一下代码:
先写一个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,装饰模式讲完了,一句话总结一下:装饰模式是为已有功能动态添加更多功能的一种方式
好了,今天就到这吧,下一篇,会讲 代理模式
本系列将持续更新,喜欢的小伙伴可以点一下关注和推荐,谢谢大家的支持。