单独看UML类图,有没有发现观察者类图和工厂方法模式的类图和相似?不过代码实现可不一样哦,从UML类图中我获取如下信息:
1、Subject是个抽象类,而不是接口
2、Observer也是个抽象类
3、具体观察者和具体通知者有一种关联关系
下面我会分别对Subject使用接口和抽象类实现,并进行比较:
首先使用接口:
抽象观察者:
package com.sjmx.observer.theory; public interface Watcher { public void update(String str); }
具体观察者:
package com.sjmx.observer.theory; public class ConcreteWatcher implements Watcher { @Override public void update(String str) { if("学校卫生评比开始".equals(str)){ System.out.println(str + ",结束打扫卫生!"); } } }
package com.sjmx.observer.theory; public class ConcreteWatcher2 implements Watcher { @Override public void update(String str) { if("学校卫生评比开始".equals(str)){ System.out.println(str + ",结束室外活动区整理!"); } } }
package com.sjmx.observer.theory; public class ConcreteWatcher3 implements Watcher { @Override public void update(String str) { if("学校卫生评比开始".equals(str)){ System.out.println(str + ",全体安静自习!"); } } }
主题(通知)接口:
package com.sjmx.observer.theory; public interface Watched { public void addWatcher(Watcher watcher); public void removeWatcher(Watcher watcher); public void notifyWatchers(String str); }
具体通知对象:
package com.sjmx.observer.theory; import java.util.ArrayList; import java.util.List; public class ConcreteWatched implements Watched { // 存放观察者 private List<Watcher> list = new ArrayList<Watcher>(); @Override public void addWatcher(Watcher watcher) { list.add(watcher); } @Override public void removeWatcher(Watcher watcher) { list.remove(watcher); } @Override public void notifyWatchers(String str) { // 自动调用实际上是主题进行调用的 for (Watcher watcher : list) { watcher.update(str); } } }
客户端代码:
package com.sjmx.observer.theory; public class Client { public static void main(String[] args) { Watched girl = new ConcreteWatched(); Watcher watcher1 = new ConcreteWatcher(); Watcher watcher2 = new ConcreteWatcher2(); Watcher watcher3 = new ConcreteWatcher3(); girl.addWatcher(watcher1); girl.addWatcher(watcher2); girl.addWatcher(watcher3); girl.notifyWatchers("warning"); } }
运行结构:
看完代码:
1、观察者模式所做的工作就是解除耦合,让耦合的双方都依赖于抽象,而不依赖于实体。从而使得一方的 变化不会影响另一方。
2、缺点也很容易看见的,所有的观察者实体都要方法相同,这在显示生活中是不可能的。比如,今天放假,学生停课,工人停止生产,股票停止交易,服务业开始服务等等。
3、抽象通知者是个接口,那么每个实体通知者都要拥有一个存放Watcher的List存在,而且每个实体通知者都有相同的addWatcher和removeWatcher方法,太累赘,而使用抽象类的话,就可以把这些相同的东西抽象到抽象类中操作,使其固定成模板。
下面再使用抽象类对通知者进行改造,观察者不变:
主题(通知)抽象类:
package com.sjmx.observer.theory2; import java.util.ArrayList; import java.util.List; import com.sjmx.observer.theory.Watcher; public abstract class Watched { // 存放观察者 private List<Watcher> list = new ArrayList<Watcher>(); public void addWatcher(Watcher watcher) { list.add(watcher); } public void removeWatcher(Watcher watcher) { list.remove(watcher); } public void notifyWatchers(String str) { // 自动调用实际上是主题进行调用的 for (Watcher watcher : list) { watcher.update(str); } } }
具体通知者:
package com.sjmx.observer.theory2; public class ConWatched extends Watched { public String state; public void notiyW(){ this.notifyWatchers(state); } }
从以上代码清晰可见,具体的通知者代码大量简化,此时Watched抽象类如果有10个实现类,代码量立刻就能体现出来,大大的减少了冗余代码
在来看看客户端:
package com.sjmx.observer.theory2; import com.sjmx.observer.theory.ConcreteWatcher; import com.sjmx.observer.theory.ConcreteWatcher2; import com.sjmx.observer.theory.ConcreteWatcher3; import com.sjmx.observer.theory.Watcher; public class Client { public static void main(String[] args) { ConWatched wt = new ConWatched(); Watcher watcher1 = new ConcreteWatcher(); Watcher watcher2 = new ConcreteWatcher2(); Watcher watcher3 = new ConcreteWatcher3(); wt.addWatcher(watcher1); wt.addWatcher(watcher2); wt.addWatcher(watcher3); wt.state = "学校卫生评比开始"; wt.notiyW(); } }
通过客户端代码比较,我发现如果使用使用抽象类实现的话,具体通知者无法使用多态,因为具体通知者有自己独特的方法而抽象类中根本就不具有这些方法。而即使使用接口实现可以使用多态,在客户端也还是要去认识每一个实现了接口Watched的实现类, 单单在代码耦合度上,没有任何进步!当然,你也可以在Watched抽象类中添加一个抽象方法,然后具体的通知者(主题)再去实现这些抽象方法,再由这些方法去调用抽象类中notifyWatchers方法,那么在客户端也就可以使用多态了!
总结:观察者模式中,通知者使用抽象类比使用接口要好,因为他们有一些公共的逻辑!