经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
设计模式之观察者模式(observer pattern)
来源:cnblogs  作者:alpha_panda  时间:2019/6/29 22:04:16  对本文有异议

观察者模式主要用于处理对象间的一对多的关系,是一种对象行为模式。该模式的实际应用场景比较容易确认,当一个对象状态发生变化时,所有该对象的关注者均能收到状态变化通知,以进行相应的处理。
本文希望通过简单的介绍和分析,能让读者对观察者模式有一个简单直观的认识和感知,以便在实际开发中根据需要灵活运用。

1. 目的

建立对象间一对多的关联关系,并能使一个对象的变化被所有关联对象感知。

2. 动机

建立一套低耦合的消息触发机制。

3. 优缺点

优点:

  1. 被观察者和观察者之间是抽象耦合的;
  2. 耦合度较低,两者之间的关联仅仅在于消息的通知;
  3. 被观察者无需关心他的观察者;
  4. 支持广播通信;

缺点:

  1. 观察者只知道被观察对象发生了变化,但不知变化的过程和缘由;
  2. 观察者同时也可能是被观察者,消息传递的链路可能会过长,完成所有通知花费时间较多;
  3. 如果观察者和被观察者之间产生循环依赖,或者消息传递链路形成闭环,会导致无限循环;

4. 应用场景

  • 需要在系统中建立一个单项广播的触发机制;
  • 系统中某个对象的行为会影响若干其他对象;
  • 对象之间的关联关系可以在运行时动态的建立与撤销;
  • 对象之间的关联关系呈现出一种树状结构;

5.  原理

下面是GoF介绍的典型的类观察者模式的UML类图:

Subject:

 抽象被观察者,仅提供注册和删除观察者对象的接口声明。

ConcreteSubject:

 具体被观察者对象,该对象中收集了所有需要被通知的观察者,并可以动态的增删集合中的观察者。当其状态发生变化时会通知所有观察者对象。

Observer:

 抽象观察者,为所有观察者定义获得通知的统一接口;

ConcreteObserver:

 观察者对象,其关注对象为Subject,能接受Subject变化时发出的通知并更新自身状态。

6.实现

接下来先将上面的UML类图转换为具体的代码,然后在举一个具体的例子来看一下其应用。

抽象被观察者类:Subject

  1. public interface Subject {
  2. public void setState(int state);
  3. public int getState();
  4. public void attach(Observer obs);
  5. public void detach(Observer obs);
  6. public void notify(String msg);
  7. }

 抽象观察者类:Observer

  1. public interface Observer {
  2. public void update(String msg);
  3. }

具体被观察者类:ConcreteSubject

  1. public class ConcreteSubject implements Subject {
  2. private List<Observer> observerList = new ArrayList<Observer>();
  3. private int state;
  4. @Override
  5. public void setState(int state) {
  6. this.state = state;
  7. notify("new state: " + state);
  8. }
  9. @Override
  10. public int getState() {
  11. // TODO Auto-generated method stub
  12. return 0;
  13. }
  14. @Override
  15. public void attach(Observer obs) {
  16. // TODO Auto-generated method stub
  17. observerList.add(obs);
  18. }
  19. @Override
  20. public void detach(Observer obs) {
  21. // TODO Auto-generated method stub
  22. observerList.remove(obs);
  23. }
  24. @Override
  25. public void notify(String msg) {
  26. // TODO Auto-generated method stub
  27. for (Observer obs: observerList) {
  28. obs.update(msg);
  29. }
  30. }
  31. }

具体观察者类:ConcreteObserver

  1. public class ConcreteObserver implements Observer {
  2. @Override
  3. public void update(String msg) {
  4. // TODO Auto-generated method stub
  5. System.out.println("ConcreteObserver receive notify msg: " + msg);
  6. }
  7. }

演示:

  1. public class Demo {
  2. public static void main(String[] args) {
  3. ConcreteObserver obs = new ConcreteObserver();
  4. ConcreteSubject sub = new ConcreteSubject();
  5. sub.attach(obs);
  6. sub.setState(666);
  7. sub.notify("just test subject notify function!");
  8. }
  9. }

结果:

  1. ConcreteObserver receive notify msg: new state: 666
  2. ConcreteObserver receive notify msg: just test subject notify function!

7.实例

我们以一个更加实际的例子——商品价格的变动来体会一下观察者模式的用途。

在网上购物的时候,商品一般都有一个价格变动通知,前提是我们关注了该商品。

这里我们稍微变通一下,只有当关注的商品价格下降,且低于用户期望购买价格的时候,才会给用户发送一条商品降价的短信通知。

下面分别定义每个类:

产品抽象类:Product

  1. public interface Product {
  2. public void setPrice(int price);
  3. public int getPrice();
  4. public void follow(User user);
  5. public void unfollow(User user);
  6. public void notifyLowPrice();
  7. }

用户抽象类:User

  1. public interface User {
  2. public boolean isExpectedPrice(int price);
  3. public void shortMSG(String msg);
  4. }

商品笔记本电脑:Laptop

  1. public class Laptop implements Product {
  2. private List<User> followList = new ArrayList<User>();
  3. private int curPrice;
  4. @Override
  5. public void setPrice(int price) {
  6. curPrice = price;
  7. System.out.println("set laptop price: " + price);
  8. notifyLowPrice();
  9. }
  10. @Override
  11. public int getPrice() {
  12. return curPrice;
  13. }
  14. @Override
  15. public void follow(User user) {
  16. followList.add(user);
  17. }
  18. @Override
  19. public void unfollow(User user) {
  20. followList.remove(user);
  21. }
  22. @Override
  23. public void notifyLowPrice() {
  24. String msg = "" + curPrice;
  25. for (User user: followList) {
  26. if (user.isExpectedPrice(curPrice)) {
  27. user.shortMSG(msg);
  28. }
  29. }
  30. }
  31. }

关注笔记本电脑用户类:LaptopBuyer

  1. public class LaptopBuyer implements User {
  2. private int expectedPrice;
  3. private String userName;
  4. public LaptopBuyer(String userName, int expectedPrice) {
  5. this.userName = userName;
  6. this.expectedPrice = expectedPrice;
  7. }
  8. @Override
  9. public boolean isExpectedPrice(int curPrice) {
  10. // TODO Auto-generated method stub
  11. return curPrice <= expectedPrice;
  12. }
  13. @Override
  14. public void shortMSG(String msg) {
  15. // TODO Auto-generated method stub
  16. System.out.println("Your follow product have a low price: " + msg + " TO:" + userName);
  17. }
  18. }

演示:

  1. public class Demo {
  2. public static void main(String[] args) {
  3. LaptopBuyer Alice = new LaptopBuyer("Alice", 6000);
  4. LaptopBuyer Jack = new LaptopBuyer("Jack", 6500);
  5. Laptop laptop = new Laptop();
  6. laptop.follow(Alice);
  7. laptop.follow(Jack);
  8. laptop.setPrice(7000);
  9. laptop.setPrice(6500);
  10. laptop.setPrice(6000);
  11. laptop.unfollow(Jack);
  12. laptop.setPrice(5999);
  13. laptop.setPrice(6099);
  14. }
  15. }

结果:

  1. set laptop price: 7000
  2. set laptop price: 6500
  3. Your follow product have a low price: 6500 TO:Jack
  4. set laptop price: 6000
  5. Your follow product have a low price: 6000 TO:Alice
  6. Your follow product have a low price: 6000 TO:Jack
  7. set laptop price: 5999
  8. Your follow product have a low price: 5999 TO:Alice
  9. set laptop price: 6099

上面的这个例子是一个能够很好地解释观察者模式的一个实际用途。

8. 总结

相比较与观察者模式,我们或许有许多获取另外一个对象状态的方式,比如,常见的轮询方式,或者仅仅在需要的时候去查一下对方的状态等,不过观察者模式有其特殊的用途,而且更加灵活。

该模式原理比较简单直接,但是实际使用过程中需要考虑一些细节问题:

  • 何时通知?
  • 有谁触发通知?
  • 观察者是关注状态变化的次数还是最终的状态?
  • 如果消息通知被阻塞,应该怎么办?
  • 是否可以改为异步消息通知?

上面这些都是实际使用时应该考虑的。考虑清楚这些细节才能更灵活的应用该模式解决实际问题。

参考:

GoF《Design Patterns: Elements of Reusable Object-Oriented Software》

https://www.runoob.com/design-pattern/observer-pattern.html

原文链接:http://www.cnblogs.com/yssjun/p/11107038.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号