观察者<Observer>模式,是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。其中两个重要对象就是观察者和被观察者。观察者内部处理观察者感兴趣的事情,而当观察者感兴趣的事情发生时,被观察者通知观察者。

  还是用模拟用户订阅彩票发布机构来说明。先实现彩票中奖号码发布结构。


设计模式—观察者模式_ide设计模式—观察者模式_设计模式_02View Code


  观察者订阅的客户。


设计模式—观察者模式_ide设计模式—观察者模式_设计模式_02View Code


//要注册的方法,查看自己是否中奖
public void Check(LotteryPublisher publisher)
{
if (myNumber == publisher.Number)
{
Console.WriteLine("{0}:哦也,我中奖了,哈哈哈!!!", name);
}
else
{
Console.WriteLine("{0}:TMD小日本,又没中~~~", name);
}
}


 

开始摇奖



设计模式—观察者模式_软件设计模式_05

static void Main(string[] args)
{
LotteryPublisher lottery = new LotteryPublisher();
Client c1 = new Client("565565656565", "张三");
Client c2 = new Client("453453453453", "李四");
Client c3 = new Client("585675656565", "王五");
Client c4= new Client("8765464564564", "赵六");
Client c5= new Client("1286876456443", "小样");
lottery.Add_Cliet(c1);
lottery.Add_Cliet(c2);
lottery.Add_Cliet(c3);
lottery.Add_Cliet(c4);
lottery.Add_Cliet(c5);
lottery.Trigger();
Console.ReadKey();
}


设计模式—观察者模式_软件设计模式_05


设计模式—观察者模式_软件设计模式_07

不过在上面的代码中我们可以看到观察者和被观察者之间存在很紧密耦合,在被观察者LotteryPublisher中直接依赖于观察者Client的实现,当我们要新添加一种观察者的时候就不得不修改LotteryPublisher,这样来说就是一种很糟糕的设计。同时当我们想增加一种新的彩票中奖号码发布结构的话,还要修改客户端代码,因为在观察者内部也依赖于被观察者。下面来进行改进。

  首先观察者都要对从被观察者获得中奖号码进行处理,我们可以抽象出一个借口。然后被观察者都有产生中奖号码和公布中奖号码这个行为,虽然具体可能不同,还有他们都有中奖号码这个属性,我们可以抽象出一个抽象类来。下面上类图

设计模式—观察者模式_ide_08

  抽象的被观察者基类。


设计模式—观察者模式_ide设计模式—观察者模式_设计模式_02View Code


public abstract class Publisher
{
//中奖号码
private string number;

public string Number
{
get { return number; }
set { number = value; }
}
//观察者列表
protected List<Ibserver> list = new List<Ibserver>();

public Publisher()
{
Number = GetNumber();
}
//添加添加观察者
public void Add_Cliet(Ibserver c)
{
list.Add(c);
}
//移除观察者
public void Remove_Client(Ibserver c)
{
list.Remove(c);
}
//发布中奖号码,virtual的子类可以重写
protected virtual void OnPublisher()
{
if (list != null)
{
Console.WriteLine("本期中奖号码为{0}", Number);
foreach (var item in list)
{
item.Processo(this);
}
}
}
//开始摇奖,virtual的子类可以重写
public virtual void Trigger()
{
OnPublisher();
}
//摇号(随机产生九位数字),virtual的子类可以重写
protected virtual string GetNumber()
{
string num = "";
Random random = new Random();
for (int i = 0; i < 9; i++)
{
num += random.Next(9).ToString();
}
return num;
}


public void Processo(string num)
{
throw new NotImplementedException();
}
}


 

  具体的被观察者(体彩和福彩)。


设计模式—观察者模式_ide设计模式—观察者模式_设计模式_02View Code


//被观察者(体彩彩票中奖号码发布机构)
public class Lottery1Publisher : Publisher
{
protected override void OnPublisher()
{
if (list != null)
{
Console.WriteLine("我是体育彩票,本期中奖号码为{0}", Number);
foreach (var item in list)
{
item.Processo(this);
}
}
}
}


 


设计模式—观察者模式_ide设计模式—观察者模式_设计模式_02View Code


protected override void OnPublisher()
{
if (list != null)
{
Console.WriteLine("我是福彩票,本期中奖号码为{0}", Number);
foreach (var item in list)
{
item.Processo(this);
}
}
}


 

  观察者的借口,定义了观察者要进行的操作。



public interface Ibserver
{
void Processo(Publisher publisher);
}


  观察者(客户和公布中奖信息的网站)


设计模式—观察者模式_ide设计模式—观察者模式_设计模式_02View Code


public class Client:Ibserver
{
//客户姓名
private string name = "";
//客户自己选的号码
private string myNumber = "";
public Client(string number, string name)
{
this.myNumber = number;
this.name = name;
}
//要注册的方法,查看自己是否中奖
public void Processo(Publisher publisher)
{
if (myNumber == publisher.Number)
{
Console.WriteLine("{0}:哦也,我中奖了,哈哈哈!!!", name);
}
else
{
Console.WriteLine("{0}:TMD小日本,又没中~~~", name);
}
}
}


设计模式—观察者模式_ide设计模式—观察者模式_设计模式_02View Code


public class WebClient : Ibserver
{
//网站公布中奖信息
public void Processo(Publisher publisher)
{
Console.WriteLine("彩票网:本期中奖号码为" + publisher.Number);
}
}


这样观察者和被观察者之间的依赖降低到了最小。通过观察者模式可以使一个类需要依靠另一个类的状态改变而自身进行一些动作的实现变的耦合度很低。