设计模式——观察者模式

观察者模式(Observer),既观察者根据被观察者“内容”上的改变而做出一系列响应,且被观察者以及观察者之间的一对多的松散耦合关系。

出版社问题:

出版社(Publisher)每天都在发布两种刊物《时尚周刊》、《大众软件》,刊物都是以批发的形式送到书店手里,书店每一段时间都能得到一批当期的刊物进行零售。书店有新华书店、地坛书店等,这个流程应该怎么设计呢?

分析一下提到的几条信息:

出版社(1)、书店(N)、《时尚周刊》、《大众软件》

《时尚周刊》和《大众软件》显然是“被更新”的“内容”,出版社和书店显然是操作这些“内容”的实体,所以不用需要关注这些“内容”,我们来关注一下我们需要的实体。

出版社将刊物到书店中出去–>调用书店的UpdatePublication(FashionWeekly,POPSoft)

xinHua.UpdatePublication(FashionWeekly,POPSoft)

ditan.UpdatePublication(FashionWeekly,POPSoft)

书店再将其装载到货架上。这显然不是什么好办法,在出版社中出现了书店的实例,也就意味着出版社与书店之间是紧耦合的关系,当需要新增另一书店的时候就需要修改原有代码了。

我们可以利用接口了。首先零售商完成的任务都是更新刊物,然后放在货架上展示给用户。

一、出版社需要一个书店的列表,这样他就知道报刊往哪里送了。

二、出版社要建立相应的注册和移除书店的机制,这样可以灵活掌握书店的签约/解约。

三、出版社需要再更新书籍并将书籍送至书店手里。

来分别看一下代码

IPublisher声明了出版社所需要实现的职能

   interface IPublisher

   {

       //注册一个书店

       void RegisterBookstore(IBookstore bookstore);

       //解约一个书店

       void RemoveBookstore(IBookstore bookstore);

       //推送杂志

       void PushPublication();

       //更新书籍信息

       void SetPublication(string fashionWeekly, string strPOPSoft);

   }

Publisher实现了IPublisher接口,因为我们要反复的插入和移除书店的列表,所以这里采用散列链表,若要使用List的话,时间一长,书店一多,性能就会成为一个大问题。用散列链表标记出我们出版社需要通知的书店,用推送循环调用IBookstore引用的Bookstore中的更新方法,使得将现有杂志版本的更新移交到书店处理。

   class Publisher : IPublisher

   {

       HashSet<IBookstore> list;

       string fashionWeekly;

       string strPOPSoft;

       //构造器

       public Publisher()

       {

           list = new HashSet<IBookstore>();

       }

       //注册

       public void RegisterBookstore(IBookstore bookstore)

       {

           if (bookstore!=null)

           {

               this.list.Add(bookstore);

           }

       }

       //解约

       public void RemoveBookstore(IBookstore bookstore)

       {

           if (bookstore != null&&this.list.Contains(bookstore))

           {

               this.list.Remove(bookstore);

           }

       }

       //推送

       public void PushPublication()

       {

           if (list.Count != 0)

           {

               foreach (IBookstore bookstore in list)

               {

                   bookstore.UpdatePublication(this.fashionWeekly,this.strPOPSoft);

               }

           }

       }

       //设置新刊物

       public void SetPublication(string fashionWeekly, string strPOPSoft)

       {

           this.strPOPSoft = strPOPSoft;

           this.fashionWeekly = fashionWeekly;

           this.PushPublication();

       }

   }

书店则需要完成的任务有:更新信息,显示信息。

<pre escaped="true" lang="chsarp" line="1">    interface IBookstore

   {

       //更新杂志

       void UpdatePublication(string fashionWeekly, string strPOPSoft);

       //放入货架展示

       void Display();

   }

我们还需要两个不同的书店

   //新华社

   class XinHua : IBookstore

   {

       string fashionWeekly;

       string strPOPSoft;

       public void UpdatePublication(string fashionWeekly, string strPOPSoft)

       {

           this.strPOPSoft = strPOPSoft;

           this.fashionWeekly = fashionWeekly;

           this.Display();

       }

       public void Display()

       {

           Console.WriteLine("新华书店:");

           Console.WriteLine("时代周刊版本:{0}",fashionWeekly);

           Console.WriteLine("大众软件版本:{0}",strPOPSoft);

       }

   }

   //地坛

   class DiTan : IBookstore

   {

       string fashionWeekly;

       string strPOPSoft;

       public void UpdatePublication(string fashionWeekly, string strPOPSoft)

       {

           this.strPOPSoft = strPOPSoft;

           this.fashionWeekly = fashionWeekly;

           this.Display();

       }

       public void Display()

       {

           Console.WriteLine("地坛书店:");

           Console.WriteLine("时代周刊版本:{0}", fashionWeekly);

           Console.WriteLine("大众软件版本:{0}", strPOPSoft);

       }

   }

好了一切准备就绪了,我们可以尝试一下将这些对象串联起来了。

   class Program

   {

       static void Main(string[] args)

       {

           //初始化一个出版社

           IPublisher publisher = new Publisher();

           //新华社

           IBookstore xinHua = new XinHua();

           //地坛

           IBookstore diTan = new DiTan();

           //分别注册两个书店

           publisher.RegisterBookstore(xinHua);

           publisher.RegisterBookstore(diTan);

           //设置新一期的刊物特征

           publisher.SetPublication("3月刊", "4月刊");

           Console.ReadKey();

       }

   }

看一下运行结果

在代码中,实际上我们只执行了更新出版社的刊物信息的一个方法,然后各个书店分别得到推送来的数据。而出版社和书店之间又是松耦合的关系,书店可任意将自己移除推送清单,也可以将自己加入推送清单中。但推送的做法有一个弊端,就是所有的书店得到的数据都是统一的,制式的,假设有个书店卖《时代周刊》效果不太理想,不想卖时代周刊了,肿么办?肿么办?

还有一种“订阅”的方式,想想怎么实现。但在具体编程中不建议用这种方式,推送虽然有弊端,但也很好的规范了书店的“规矩”这很重要。。