设计模式——观察者模式
观察者模式(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
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();
}
}
看一下运行结果
在代码中,实际上我们只执行了更新出版社的刊物信息的一个方法,然后各个书店分别得到推送来的数据。而出版社和书店之间又是松耦合的关系,书店可任意将自己移除推送清单,也可以将自己加入推送清单中。但推送的做法有一个弊端,就是所有的书店得到的数据都是统一的,制式的,假设有个书店卖《时代周刊》效果不太理想,不想卖时代周刊了,肿么办?肿么办?
还有一种“订阅”的方式,想想怎么实现。但在具体编程中不建议用这种方式,推送虽然有弊端,但也很好的规范了书店的“规矩”这很重要。。