设计模式之观察者模式

一、扯淡

今天向实验室陆路请教了一下面试的过程以及技巧之类的问题,其中说到了奥卡姆剃刀,我初次接触这个概念还是在读卡梅隆大学那本《机器学习》时,书中在介绍归纳偏置时提到了这一概念,意思是说在保证正确性和可行性的前提下最简单的方法就是最有效的方法。其实我们在学习的过程中一样要遵从奥卡姆剃刀原则,很多事情都没有想象中那么复杂,比如linux内核,看似很高深的东西,其实内核在处理各个部分的管理时都是用的最简单有效的方法(如进程的调度策略),凡是我们遇到了一个看似非常复杂的问题,我们的第一反映应该是以怀疑的态度看待它,最现实的赤裸裸的就是你拿到一篇论文,越是看不懂故弄玄虚的可能就越大,一般国外的高质量paper写的都非常简单明了,让人读起来就感觉思路非常清晰。好了不扯了,今天我们就以奥卡姆剃刀的方法来介绍观察者模式吧。

二、什么时候需要观察者

答案是需要观察的时候(别喷我),观察的意思就是去看,看外界的状态,外界如果发生了某种变化,那观察外界的对象就执行其对应的操作,这种场景在现实应用中很常见,或者说整个社会就是在我观察你,你观察我这种机制下运行的。实际一点的例子:图形用户程序中,图形对象需要监视后台数据对象的变化来做响应的显示;再举一现在系统中常用的一个服务:短信推送服务,当用户发出了交易请求时,就给该用户发送一个短信验证码。

三、观察者模式

思考:为了实现观察者对外界对象变化做出响应,我们应该想到两种做法,一种是观察者以轮询的方式观察某一对象,当本次观察发现对象状态发生改变时就做出响应(这种效率太低,因为这意味着观察者在外界对象变化前不能做其他的事情);第二种做法是异步通知的方式,当对象状态发生改变时,对象会向观察者发送消息(在代码中体现为它调用了观察者的某一方法),因为这不像进程间通信一样可以通过中断或者信号的方式实现,对象之间的异步通信只能通过调用彼此之间的方法。这意味着什么?这意味着外界对象必须拥有观察者的指针或者引用。即他们之间应该有聚合关系。

下面我们给出一个简单的实例代码,就以父母来观察孩子为场景
首先给出一个ChinaParent类的实现
//观察者
public class ChinaParent {
	
	//孩子生病了他们会给孩子电话慰问
	public void callChildren(){
		
		System.out.println("回来吧,带你去看医生");
	}
}

然后给出Children类的实现
//被观察者
public class ChinaChildren {
	
	private ChinaParent p;
	
	public void gotSick(){
		
		p.callChildren();
	}
}
ChinaChildren和ChinaParent是一种聚合关系,当孩子生病的时候会主动的调用观察这p的callChildren()方法,以表示通知了他的观察者。
但是这种观察者的实现方式很不好,因为我们在具体的实现类之间建立的很强的关联关系,我们需要对他们进行解耦,而面向对象中解耦的方法最好不过的是面向接口的设计方法了,所以我们给这两个对象都实现某一接口
public interface Parent {
	
	public void callChildren();
}

public interface Children {
	
	public void notifyParent();
}
然后具体的实现类实现对应的接口
//观察者
public class ChinaParent implements Parent{
	
	//孩子生病了他们会给孩子电话慰问
	public void callChildren(){
		
		System.out.println("回来吧,带你去看医生");
	}
}
//被观察者
public class ChinaChildren implements Children{
	
	private Parent p;
	
	public void notifyParent() {
		
		p.callChildren();
	}
	
	public void setObserver(Parent p){
		this.p = p;
	}
}



这样就把孩子和具体的Parent解耦了,现在当有新类型的Parent时,我们也不用担心为了将新的观察者加入到我们的系统中而修改源码了。如:
//观察者
public class AmericaParent implements Parent{
	
	//孩子生病了他们会给孩子电话慰问
	public void callChildren(){
		
		System.out.println("自己去看医生");
	}
}
到此为止其实你已经掌握了观察者模式的核心了,说白了就是用面向接口的方法将具体的观察者和被观察者对象解耦了,但是实际项目中我们还会发现对象往往可以由多个对象共同观察,所以被观察对象中应该有多个观察者的引用
首先我们改变Children接口
public interface Children {
	
	public void notifyAllParents();
	
}
然后修改对应的被观察者
import java.util.ArrayList;

//被观察者
public class ChinaChildren implements Children{
	
	private ArrayList<Parent> ps;
	
	public void notifyAllParents() {
		
		for(int i=0;i<ps.size();i++){
			
			ps.get(i).callChildren();
			
		}
		
	}
	
	public void addObserver(Parent p){
		ps.add(p);
	}
	
	public void delObserver(Parent p){
		ps.remove(p);
	}
	
}
这种就是最常用的观察者模式了,其实就是实现了多观察者同时观察同一个对象,人们美其名曰:发布订阅。因为被观察对象给出了向该对象中注册新的观察者和删除已有观察者的接口。

四、总结

设计模式中有很多模式都是通过面向接口编程的思想来对强关联的对象之间解耦的,面向接口给我们的程序带来了更好的扩展性,而这种方法运用到不同的场景语义中便形成了不同的模式,也就是说很多模式不是技术上的不同(都是面向接口而已),而是将面向接口的思想运用在了不同的场景应用中而已。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值