c#设计模式之观察者模式(Observer Pattern)

场景出发

一个月高风黑的晚上,突然传来了尖锐的猫叫,宁静被彻底打破,狗开始吠了,大人醒了,婴儿哭了,小偷跑了

这个过程,如果用面向对象语言来描述,简单莫过于下:

 1    public class Cat
 2     {
 3         public void Miao()
 4         {
 5             Console.WriteLine("猫叫了..........");
 6 
 7             new Dog().Wang();
 8             new Parents().Wake();
 9             new Baby().Cry();
10             new Thief().Run();
11         }
12     }
13     public class Dog
14     {
15         public void Wang()
16         {
17             Console.WriteLine("狗汪了..........");
18         }
19     }
20 
21     public class Parents
22     {
23         public void Wake()
24         {
25             Console.WriteLine("大人醒了..........");
26         }
27     }
28 
29     public class Baby
30     {
31         public void Cry()
32         {
33             Console.WriteLine("婴儿哭了..........");
34         }
35     }
36 
37     public class Thief
38     {
39         public void Run()
40         {
41             Console.WriteLine("小偷跑了..........");
42         }
43     }
Object
1     class Program
2     {
3         static void Main(string[] args)
4         {
5             Cat cat = new Cat();
6             cat.Miao();
7         }
8     }
Main

代码分析

分析上述代码,显然违背了面向对象语言几大原则中的:

1单一职责原则:猫这个对象的内部还依赖其他对象细节,致使其本来的职责与其他对象的职责耦合在一起

2开闭原则(Open-Closed Principle,OCP):即对扩展开放,对修改关闭,很显然,如果我们要扩展,例如改变上述动作的顺序,那么我们就不得不去修改猫内部代码

上述代码问题的核心:猫这个对象很不稳定,在我们整个过程中,猫应该是独立存在,且不能够修改的

观察者模式

针对上面的问题场景,在面向对象语言中有一种适用的设计模式——观察者模式

观察者模式将上述对象分为2类:

主题(Subject):被关注的对象,当它的状态发生变化时,会触发观察者的反应,上述的猫就是一个主题,它发出猫叫这个状态变化的信号

观察者(或订阅者,Observer):即关注主题的对象,当主题的状态发生变化,它们会做出反应,上述的狗,大人,婴儿,小偷都是观察者

观察者模式采用的方式是将主题对观察者的直接依赖,转为对它们的抽象的依赖,具体代码如下:

1    /// <summary>
2     /// 观察者的抽象
3     /// </summary>
4     public interface IObserver
5     {
6         void Action();     
7     }
IObserver
 1     /// <summary>
 2     /// 主题
 3     /// </summary>
 4     public class Cat
 5     {
 6         /// <summary>
 7         /// 观察者集合
 8         /// </summary>
 9         private List<IObserver> _observerList = new List<IObserver>();
10         /// <summary>
11         /// 添加观察者
12         /// </summary>
13         /// <param name="observer"></param>
14         public void AddObserver(IObserver observer)
15         {
16             _observerList.Add(observer);
17         }
18         /// <summary>
19         /// 状态变化
20         /// </summary>
21         public void Miao()
22         {
23             Console.WriteLine("猫叫了..........");
24 
25             ///后续动作
26             foreach (var item in _observerList)
27             {
28                 item.Action();
29             }
30         }
31     }
Subject
 1     public class Dog : IObserver
 2     {
 3         public void Wang()
 4         {
 5             Console.WriteLine("狗汪了..........");
 6         }
 7         public void Action()
 8         {
 9             Wang();
10         }
11     }
12 
13     public class Parents : IObserver
14     {
15         public void Wake()
16         {
17             Console.WriteLine("大人醒了..........");
18         }
19         public void Action()
20         {
21             Wake();
22         }
23     }
24 
25     public class Baby : IObserver
26     {
27         public void Cry()
28         {
29             Console.WriteLine("婴儿哭了..........");
30         }
31         public void Action()
32         {
33             Cry();
34         }
35     }
36 
37     public class Thief : IObserver
38     {
39         public void Run()
40         {
41             Console.WriteLine("小偷跑了..........");
42         }
43         public void Action()
44         {
45             Run();
46         }
47     }
Observers
 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Cat cat = new Cat();
 6             cat.AddObserver(new Dog());
 7             cat.AddObserver(new Parents());
 8             cat.AddObserver(new Baby());
 9             cat.AddObserver(new Thief());
10             cat.Miao();
11             Console.ReadKey();
12         }
13     }
Main

通过观察者模式,主题与观察者之间的依赖被转移到了接口之上,面对新的扩展,完全不用再去修改主题,当我们需要添加新的观察者,只需要实现IObserver这个接口就可以,当我们需要修改顺序的时候,只需要在上端改变添加的顺序就可以

观察者模式的类图:

观察者模式存在的3种角色:

1主题角色(Cat):具体观察者角色关注的对象,控制状态的改变

2抽象观察者角色(IObserver):具体观察者角色的抽象,将主题对具体观察者类的依赖转移到抽象上来

3具体观察者角色(Dog,Parents,Baby,Thief):受主题状态改变影响的具体类,在主题状态改变后,做出相应动作

Csharp下更优选择

相比于上述使用观察者模式解决问题,在.Net里有更加优秀的解决方案,那就是委托事件

同样是上述这个问题,使用委托事件来解决,代码如下

 1     public class Cat
 2     {
 3         public event Action MiaoEvent;
 4         public void Miao()
 5         {
 6             Console.WriteLine("猫叫了..........");
 7 
 8             MiaoEvent.Invoke();
 9         }
10     }
Subject
 1    public class Dog
 2     {
 3         public void Wang()
 4         {
 5             Console.WriteLine("狗汪了..........");
 6         }
 7     }
 8 
 9     public class Parents
10     {
11         public void Wake()
12         {
13             Console.WriteLine("大人醒了..........");
14         }
15     }
16 
17     public class Baby
18     {
19         public void Cry()
20         {
21             Console.WriteLine("婴儿哭了..........");
22         }
23     }
24 
25     public class Thief
26     {
27         public void Run()
28         {
29             Console.WriteLine("小偷跑了..........");
30         }
31     }
ObjectForAction
 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Cat cat = new Cat();
 6             cat.MiaoEvent += new Dog().Wang;
 7             cat.MiaoEvent += new Parents().Wake;
 8             cat.MiaoEvent += new Baby().Cry;
 9             cat.MiaoEvent += new Thief().Run;
10             cat.Miao();
11             Console.ReadKey();
12         }
13     }
Main

 使用观察者模式,主题内部维护的是一堆观察者的抽象对象,使用事件的方式,主题内部维护的是一张方法列表

适用场景和优劣势

当存在一对多的依赖关系,且需要监听状态变化的时候,观察者模式是一个很好的解决方案,例如:服务订阅通知,警报监控等等

优势:

1隔离了对象之间的直接依赖,降低了程序的耦合度

2增加了程序的可读性,方便了维护和扩展

3不需要熟悉观察者的细节详情,只用实现接口就可以

劣势:

1增加了程序的复杂度,设计模式的通病

2观察者模式的效率是个问题,一个后续动作出现问题,会堵塞整个过程

猜你喜欢

转载自www.cnblogs.com/banluduxing/p/9279571.html
今日推荐