复合模式是HeadFirst上面详细讲的最后一个模式,其前面的模式作者认为都是成熟的经常使用的模式。所以这是详细讲解模式的最后一篇,同时这个模式讲解的篇幅也是最长的。复合模式顾名思义就是使用其他模式联合使用解决问题,但是将某些模式结合使用并不代表这些模式就能称为复合模式。复合模式必须够一般性,适合解决许多问题。相信我们大家都知道的MVC就是复合模式的应用,那么我们就来看模式如何结合使用和MVC中使用到的模式。
模式结合
记得第一个模式,策略模式就是以鸭子开头,而最后一个模式用鸭子结尾,做到首尾呼应吧。我们依然看鸭子的例子来讲解我们的复合模式。
(1)首先创建一个Quackable,然后让某些鸭子实现接口。
1 public interface Quackable 2 { 3 public void Quack(); 4 } 5 6 public class RedheadDuck : Quackable 7 { 8 public void Quack() 9 { 10 Console.WriteLine("呱呱呱"); 11 } 12 }
(2)我们写了一个红头鸭的类,然后我们再加点其他种类的鸭子,例如橡皮鸭。
1 public class RubberDuck : Quackable 2 { 3 public void Quack() 4 { 5 //橡皮鸭的叫声 6 Console.WriteLine("吱吱吱"); 7 } 8 }
(3)测试一下写的例子,测试代码
(4)当我们有鸭子的时候,我们也许有一只鹅。
1 public class Goose 2 { 3 public void Honk() { 4 Console.WriteLine("咯咯咯"); 5 } 6 }
(5)如果我们要把鹅也加入到模拟器中,那么为了统一处理我们可以使用适配器将鹅适配成鸭子
1 public class GooseAdapter : Quackable 2 { 3 private Goose goose; 4 public GooseAdapter(Goose goose) { 5 this.goose = goose; 6 } 7 public void Quack() 8 { 9 goose.Honk(); 10 } 11 }
(6)当我们适配成鸭子后在模拟器中加入鹅
(7)如果我们要知道叫声的次数我们需要一个装饰者,通过把鸭子包装进装饰者对象,给鸭子一些新行为(计算次数的行为)。我们不用修改鸭子的代码。
1 public class QuackCounter : Quackable 2 { 3 Quackable duck; 4 private static int numberOfQuacks; 5 public QuackCounter(Quackable duck) 6 { 7 this.duck = duck; 8 } 9 10 public void Quack() 11 { 12 duck.Quack(); 13 numberOfQuacks++; 14 } 15 16 public static int GetQuacks() 17 { 18 return numberOfQuacks; 19 } 20 }
(8)包装实例化Quackable,统计叫声次数。
(9)对于装饰和没被装饰的鸭子我们想分别管理,让创建的和装饰部分包装起来。我们需要创建一个工厂,而且是不同类型鸭子的产品家族,所以我们要用抽象工厂模式。
1 /// <summary> 2 /// 定义抽象工厂,由子类创建不同的家族 3 /// </summary> 4 public abstract class AbstractDuckFactory 5 { 6 public abstract Quackable CreateReadheadDuck(); 7 public abstract Quackable CreateRubberDuck(); 8 } 9 10 11 /// <summary> 12 /// 没有装饰者的工厂 13 /// </summary> 14 public class DuckFactory : AbstractDuckFactory 15 { 16 public override Quackable CreateReadheadDuck() 17 { 18 return new RedheadDuck(); 19 } 20 21 public override Quackable CreateRubberDuck() 22 { 23 return new RubberDuck(); 24 } 25 } 26 27 28 /// <summary> 29 /// 有装饰者的工厂 30 /// </summary> 31 public class CountingDuckFactory : AbstractDuckFactory 32 { 33 public override Quackable CreateReadheadDuck() 34 { 35 return new QuackCounter(new RedheadDuck()); 36 } 37 38 public override Quackable CreateRubberDuck() 39 { 40 return new QuackCounter(new RubberDuck()); 41 } 42 }
(10)使用工厂模式
(11)有了工厂模式统一创建鸭子,我们还可以统一管理鸭子,而组合模式允许我们像对待单个对象一样对待对象集合。组合需要和叶节点元素一样实现相同的接口,这里的叶节点就是Quackable。
1 public class Flock : Quackable 2 { 3 private List<Quackable> quackables = new List<Quackable>(); 4 5 public void Add(Quackable quackable) { 6 quackables.Add(quackable); 7 } 8 public void Quack() 9 { 10 var enumerator= quackables.GetEnumerator(); 11 while (enumerator.MoveNext()) { 12 Quackable quackable = enumerator.Current; 13 quackable.Quack(); 14 15 } 16 } 17 }
(12)在这个组合模式中我们遍历鸭子的叫声的时候用到了foreach循环的本质方法,这里实际是另外一个迭代器模式。
然后我们改造一下测试模拟器,看看输出结果。
(13)最后我们还整合一个需求,当有人想要观察鸭子的行为,我们可以给鸭子加上一个观察者模式。
观察者需要一个Observable接口,所谓的Observable就是被观察的对象。Observable需要注册和通知观察者的方法。
1 public interface QuackObservable 2 { 3 public void RegisterObserver(Observer observer); 4 public void NotifyObservers(); 5 }
鸭子要实现接口QuackObservable,由于鸭子都实现了Quackable接口,所以我们可以让Quackable实现QuackObservable接口。
1 public interface Quackable:QuackObservable 2 { 3 public void Quack(); 4 }
(14)我们需要在鸭子的每个类中实现注册和通知,但是这里我们可以新建一个类Observable用来封装注册和通知的代码,然后将他和QuackObservable组合在一起,这样我们只需要一份注册和通知的代码,QuackObservable所有的调用都委托给Observable这个辅助类。
1 public class Observable : QuackObservable 2 { 3 //观察者 4 List<Observer> observers = new List<Observer>(); 5 QuackObservable duck; 6 7 public Observable(QuackObservable duck) { 8 this.duck = duck; 9 } 10 11 public void RegisterObserver(Observer observer) 12 { 13 observers.Add(observer); 14 } 15 16 public void NotifyObservers() 17 { 18 foreach (var observer in observers) 19 { 20 observer.Update(duck); 21 } 22 } 23 }
(15)然后我们以红头鸭为例改造被观察者,整合Quackable类和Observable。
1 public class RedheadDuck : Quackable 2 { 3 Observable observable; 4 public RedheadDuck() { 5 observable = new Observable(this); 6 } 7 public void RegisterObserver(Observer observer) 8 { 9 observable.RegisterObserver(observer); 10 } 11 12 public void NotifyObservers() 13 { 14 observable.NotifyObservers(); 15 } 16 17 public void Quack() 18 { 19 Console.WriteLine("呱呱呱"); 20 NotifyObservers(); 21 } 22 }
(16)如果是一群鸭子观察,则我们修改Flock类,注册时候注册到每个要观察的叶节点上,然后当通知的时候各自叶节点会调用自己的NotifyObservers,所有Flock的NotifyObservers就不用做任何事情。
1 public class Flock : Quackable 2 { 3 private List<Quackable> quackables = new List<Quackable>(); 4 5 public void Add(Quackable quackable) 6 { 7 quackables.Add(quackable); 8 } 9 10 public void Quack() 11 { 12 var enumerator = quackables.GetEnumerator(); 13 while (enumerator.MoveNext()) 14 { 15 Quackable quackable = enumerator.Current; 16 quackable.Quack(); 17 } 18 } 19 20 public void RegisterObserver(Observer observer) 21 { 22 foreach (var duck in quackables) 23 { 24 duck.RegisterObserver(observer); 25 } 26 } 27 28 public void NotifyObservers(){} 29 }
(17)现在完成Observer端。
1 public interface Observer 2 { 3 void Update(QuackObservable duck); 4 } 5 6 public class QuackObserver : Observer 7 { 8 public void Update(QuackObservable duck) 9 { 10 Console.WriteLine("观察者:"+duck+" 正在叫"); 11 } 12 }
(18)加入观察者测试。
通过这个例子我们组合了6个设计模式,你可能要问这就是复合模式?不,这只是一群模式携手合作。所谓的复合模式,是指一群模式被结合起来使用,以解决一般性问题。而这个例子只是为了演示如何将模式结合起来,但它不是为了解决一般性问题。
结合上面的代码我们画一个类图便于理解例子中模式如何协作结合的。
复合模式:结合两个或以上的模式,组成一个解决方案,解决以再发生的一般性问题。
MVC复合模式
M:model模型,模型持有所有的数据、状态和程序逻辑。
V:visual视图,用来呈现状态和数据,是和用户交互的界面。
C:control控制,取得用户的输入并告知模型如何作出对应的动作。
MVC包含的设计模式
策略模式:视图通过控制器对模型进行修改,视图是一个对象,可以被调整为使用不同的控制器(不同策略)。
组合模式:界面中的每个显示组件不是组合节点就是叶节点。当控制器想要做某种更新时,只需告诉视图最顶层的组件即可,组合模式会处理组合节点或叶节点的更新。
观察模式:当模型发生改变时,需要立即反馈到视图中,此时可以把控制器或视图作为观察者,观测模型的动态变化。这样模型完全独立于视图和控制器,是一个松耦合的实现。
虽然MVC中的设计模式也许不再试经典意义上的模型,但现实中设计模式都不一定照搬经典设计,会有优化或改动,所以并不影响它就是设计模式的使用。
这就是复合模式的概念和例子,HeadFirst中的所有细讲模式都已经结束,下一次我会罗列其他没有详细讲解的设计模概念,也许在其他地方使用到了我会回来补全例子和代码。