案例分析:设计模式与代码的结构特性
我选择的设计模式是观察者模式。
一、何为观察者模式
在现实世界中,许多对象并不是孤立存在的,其中一个对象的行为发生改变往往会导致一个或多个其它对象的行为跟着改变。例如在交通路口红绿灯颜色的改变会导致司机是停还是行,气象局的预报会导致人们决定自己的出行计划,猪肉价格的波动会导致人们消费猪肉的数量等。
在软件的世界中也是这样。例如在事件模型中的事件源和事件处理者;CS模型中的客户端与服务器等。接下来为观察者模式下一个精确的定义。
观察者模式是指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
二、观察者模式的优缺点
优点:
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
- 目标与观察者之间建立了一套触发机制。
缺点:
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
三、观察者模式的结构
观察者模式的主要角色如下:
- 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
- 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
四、适用观察者模式的场景
通过前面的分析与应用实例可知观察者模式适合以下几种情形。
- 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
五、具体实现
需要注意实现观察者模式时具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。
下面实现了一个微信公众号与用户的观察者模式,公众号推送消息而用户接收消息。
1 package observer; 2 import java.util.*; 3 public class ObserverPattern 4 { 5 public static void main(String[] args) 6 { 7 Subject subject=new ConcreteSubject(); 8 Observer obs1=new ConcreteObserver1(); 9 Observer obs2=new ConcreteObserver2(); 10 subject.add(obs1); 11 subject.add(obs2); 12 subject.notifyObserver(); 13 } 14 } 15 //抽象公众号 16 abstract class GZH 17 { 18 protected List<YH> yhs=new ArrayList<YH>(); 19 //增加用户方法 20 public void add(YH yh) 21 { 22 yhs.add(yh); 23 } 24 //删除用户方法 25 public void remove(YH yh) 26 { 27 yhs.remove(yh); 28 } 29 public abstract void notifyYH(); //通知用户方法 30 } 31 //具体公众号 32 class gzh extends GZH 33 { 34 public void notifyYH() 35 { 36 System.out.println("推送一条消息..."); 37 System.out.println("--------------"); 38 39 for(Object obs:yhs) 40 { 41 ((YH)obs).response(); 42 } 43 44 } 45 } 46 //抽象用户 47 interface YH 48 { 49 void response(); //反应 50 } 51 //具体用户1 52 class yh1 implements YH 53 { 54 public void response() 55 { 56 System.out.println("用户1收到消息!"); 57 } 58 } 59 //具体用户2 60 class yh2 implements YH 61 { 62 public void response() 63 { 64 System.out.println("用户二收到消息!"); 65 } 66 }
从该案例中可以看出通过抽象目标、具体目标、抽象用户和具体用户将程序间的耦合尽量解开,有利于程序的改动与移植。譬如我想更改公众号推送的内容只需要在具体公众号中更改函数即可。
GitHub:https://github.com/Darz233/ASE/blob/master/ObserverPattern