观察者模式(Observer Pattern)在项目中常常会被使用到。也被叫做公布订阅模式,也就是说 观察者 = 公布者 + 订阅者
GoF的《设计模式》中对观察者是这样描写叙述的:
Define a one-to-many dependency between objects so that when one object changes state。 all its dependents are notified and updated automatically。
意思就是。定义一个对象之间的一对多的依赖关系,使得当一个对象的状态发生改变的时候,它的全部依赖对象可以自己主动地被通知以及被更新。这里须要注意原句被动语态的使用。依赖对象不是主动检測。而是被通知和被更新。
观察者模式的UML
能够发现,观察者模式共涉及4种角色:
抽象主题角色(Subject):也就是被观察者角色,它能够在自己的内部加入或删除一个观察者对象,这些观察者对象将作为它的通知对象。
抽象观察者角色(Observer):为全部的观察者对象定义一个共同的接口,指定同样的通知规范。
详细主题角色(Concrete Subject):实现抽象主题角色,是详细真实的被观察者。
详细观察者角色(Concrete Observer):实现抽象观察者角色,是详细真实的观察者。一般有多个,能够定义自己收到被观察者通知后运行的业务逻辑。
事实上再抽象一点划分。观察者模式仅仅有两个角色:观察者和被观察者。也就是1个被观察者相应多个观察者的依赖,这也是为什么观察者 = 公布者 + 订阅者。
public interface Subject { //注冊观察者对象o public void attach(Observer o); //删除观察者对象o public void detach(Observer o); //通知观察者 public void notifyObserver(); }
抽象主题角色(被观察者角色)定义了添加和删除观察者的方法,详细的被观察者须要维护一个观察者列表,当被观察者的状态发生变化的时候,回去主动的通知列表中的观察者。
抽象观察者角色(Observer)的代码实现:
public interface Observer { //接收到通知的操作 public void update(); }
仅仅有一个update方法,用于接收到来之被观察者通知的时候採取的更新操作,这种方法一般由被观察者调用。
以下是这两个接口的详细实现,也就是详细主题角色(详细被观察者角色)、详细观察者角色
import java.util.LinkedList; import java.util.List; public class ConcreteSubject implements Subject { //用于保存自己的注冊对象 private List<Observer> observerList = new LinkedList<Observer>(); //方便常常的插入和删除操作 /** * 注冊一个观察者 */ @Override public void attach(Observer o) { // TODO Auto-generated method stub observerList.add(o); } /** * 取消一个观察者 */ @Override public void detach(Observer o) { // TODO Auto-generated method stub observerList.remove(o); } /** * 通知观察者 */ @Override public void notifyObserver() { // TODO Auto-generated method stub for(Observer obs : observerList ) { obs.update(); //逐个通知观察者 } } }
public class ConcreteObserver implements Observer { /** * 接到通知的逻辑 */ @Override public void update() { // TODO Auto-generated method stub } }
观察者模式的详细实例
一个典型的样例就是消息订阅,比方,我们在关注了一个某晚报微信公众号,然后每天公众号给我们发送订阅的新闻,这里公众号就是一个被观察者角色,而我们订阅者就是观察者角色
抽象主题角色Subject,也就是公众号的简单抽象定义:
public interface PublicOffcial { //注冊订阅消息的用户 public void attach(WechatUser user); //取消订阅消息的用户 public void detach(WechatUser user); //向用户发送订阅 public void notifyObserver(String news); }
抽象观察者角色,这里也就是微信订阅用户的简单抽象
public interface WechatUser { public void readNews(String news); }
实现详细主题角色(真实被观察者),这里是XX晚报的公众号
import java.util.LinkedList; import java.util.List; public class PublicOffcialXXNewPaper implements PublicOffcial { //已订阅用户的列表 private List< WechatUser> list = new LinkedList<WechatUser>(); /** * 当用户订阅时。增加列表 */ @Override public void attach(WechatUser user) { if( ! list.contains(user)) list.add(user); } /** * 用户取消订阅时,从列表清除 */ @Override public void detach(WechatUser user) { if(list.contains(user)) list.remove(user); } /** * 通知订阅者 */ @Override public void notifyObserver(String news) { for(WechatUser user : list ) { user.readNews(news); } } /** * 报社某天的公布新闻工作,包含发送给微信订阅者,可能还要更新站点 * @param news */ public void ReleaseNews(String news) { //something to do e.g: update website.. System.out.println("报社公布今日新闻【" + news + "】"); notifyObserver(news); } }
详细观察者角色,也就是订阅消息的用户
public class NewsPaperFan implements WechatUser { private String name; public NewsPaperFan(String name) { // TODO Auto-generated constructor stub this.name = name; } @Override public void readNews(String news) { System.out.println("用户: " + name + "收到订阅的新闻【" + news + "】"); } }
以上就是关于消息订阅的观察者模式的实现。
client測试类:
public class Client { public static void main(String[] args) { PublicOffcialXXNewPaper newPaper = new PublicOffcialXXNewPaper(); WechatUser user1 = new NewsPaperFan("Tony"); WechatUser user2 = new NewsPaperFan("Jack"); WechatUser user3 = new NewsPaperFan("David"); newPaper.attach(user1); newPaper.attach(user2); newPaper.attach(user3); newPaper.ReleaseNews("苹果即将推出IPhone6..."); } }
程序输出:
报社公布今日新闻【苹果即将推出IPhone6...】 用户: Tony收到订阅的新闻【苹果即将推出IPhone6...】 用户: Jack收到订阅的新闻【苹果即将推出IPhone6...】 用户: David收到订阅的新闻【苹果即将推出IPhone6...】
综上,能够看出观察者模式的一些长处:
- 观察者和被观察者之间是抽象耦合的。
被观察者仅仅能观察到观察者的接口层定义。而不知道自己通知的观察者的详细类型,它们之间属于不同的抽象层面,没有紧密的耦合,easy扩展。
- 支持像消息订阅这种广播通信。
一些缺点:
- 假设被观察者须要通知多个观察者,或者通知过程时候耗时,那么上述被观察者的通知过程将会很耗时。
对于这一点。假设通知的过程是相互独立的任务。那么能够使用多线程去完毕,使通知在子线程中完毕。
- 假设拥有多个被观察者。而且它们之间存在依赖关系,那么easy发生循环调用。
观察者模式的应用场景
- 广播链问题。
一个对象事实上既能够作为观察者也能够作为被观察者,这样消息像在广播链中传递。可是须要注意假设链的长度假设过程。就会变的复杂。难以控制。
- 异步处理的问题。
在异步处理过程须要考虑线程安全和队列的问题。