观察者模式(Observer Patten)
观察者模式的定义:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新
考虑到如下一个场景,记者每次获得独家新闻,都需要通知人民日报和新华日报,Hi,i have a new message!
public class SpecificRepoter { private PeopleDaily pd; private XinHuaDaily xd; private String msg; public Repoter(){ this.pd = new PeopleDaily(); this.xd = new XinhuaDaily(); } public void gotNewNews(String msg){ this.msg = msg; } public void notifyNewspaperOffice(){ pd.update(msg); xd.update(msg); } }
public class XinHuaDaily { public void update(String message){ System.out.println("XinHua Daily got the new news!"); } }
public class PeopleDaily { public void update(String message){ System.out.println("People Daily got the new news!"); } }
由于记者和报社之间的合约关系由记者来维护,维护方式为:和记者签订合同的报社作为记者的属性,也即具体报社类和记者类紧耦合,故合约关系的变化必然导致记者类的变化:
现在记者和人民日报的合约到期,同时和环球时报签订了新合约
顺承前面的设计方式,必须要修改记者类以适应合约关系的改变:
public class SpecificRepoter { private XinHuaDaily xd; private GlobalTimes gt; private String msg; public Repoter(){ this.xd = new XinHuaDaily(); this.gt = new GlobalTimes(); } public void gotNewNews(String msg){ this.msg = msg; } public void notifyTheNewspaperOffice(){ xd.update(msg); gt.update(msg); } }
public class GlobalTimes { public void update(String message){ System.out.println("Global Times got the new news!"); } }
显然这种设计的可扩展性很低,本质原因是记者类和具体报社类紧耦合
要提高程序的扩展性,则要从降低记者类和具体报社类的耦合度来下手:
设计一:
1,抽象出记者接口和报社抽象类,针对接口编程
2,记者与报社之间的合约关系不再通过记者组合具体报社类来维护。设计一个List<NewspaperOffice>来保存和记者签订合同的报社(本质原则也是封装可变化部分,但只是封装至一个List属性中,而没有单独封装至一个类中,不像设计二那么彻底),记者将不和任何特定的报社类相耦合
public interface Repoter { public void registerNewspaperOffice(NewspaperOffice no); public void removeNewspaperOffice(NewspaperOffice no); public void notifyNewspaperOffice(); }
public class SpecificRepoter implements Repoter { private String msg; private List<NewspaperOffice> relation; public SpecificRepoter(){ relation = new ArrayList<NewspaperOffice>(); } public void registerNewspaperOffice(NewspaperOffice no) { if(!relation.contains(no)){ relation.add(no); } } public void removeNewspaperOffice(NewspaperOffice no) { if(relation.contains(no)){ relation.remove(no); } } public void notifyNewspaperOffice() { if(null != relation && relation.size()>0){ for(NewspaperOffice no : relation){ no.update(this.msg); } } } public void getNewNews(String msg){ this.msg = msg; notifyNewspaperOffice(); } }
public abstract class NewspaperOffice { protected Repoter repoter; protected String newMsg; public void register(){ repoter.registerNewspaperOffice(this); } public void remove(){ repoter.removeNewspaperOffice(this); } public abstract void update(String newMsg); }
public class PeopleDaily extends NewspaperOffice{ public PeopleDaily(Repoter repoter){ this.repoter = repoter; } public void update(String newMsg) { this.newMsg = newMsg; System.out.println("People Daily got "+newMsg); } }
具体报社类设计拥有register()和remove()方法以提高使用时的灵活性,当然这不是必要的
无论合约关系如何变化,SpecificReport类都不需要重新设计修改,相当方便
public class Test { public static void main(String[] args){ Repoter repoter = new SpecificRepoter(); NewspaperOffice n1 = new GlobalTimes(repoter); NewspaperOffice n2 = new XinHuaDaily(repoter); NewspaperOffice n3 = new PeopleDaily(repoter); n1.register(); n2.register(); repoter.registerNewspaperOffice(n3); repoter.getNewNews("news coming!"); n3.remove(); repoter.removeNewspaperOffice(n2); repoter.getNewNews("another news coming!"); } }
Global Times got news coming! XinHua Daily got news coming! People Daily got news coming! Global Times got another news coming!
总结一下:
我们再来看看观察者模式:
当然具体实现方式上可以有多种变化
设计二,将记者和报社的合约关系独立出去,单独维护(封装),此时记者类不但与具体报社类解耦,甚至与报社抽象类解耦:
public abstract class Repoter { protected String msg; protected Relation relation; public abstract void getNewNews(String msg); }
public class SpecificRepoter extends Repoter { public SpecificRepoter(Relation relation){ this.relation = relation; } public void getNewNews(String msg){ this.msg = msg; relation.notifyNewspaperOffice(msg); } }
public interface Relation { public void registerNewspaperOffice(NewspaperOffice no); public void removeNewspaperOffice(NewspaperOffice no); public void notifyNewspaperOffice(String msg); }
public class SpecificRelation implements Relation { private List<NewspaperOffice> relation; public SpecificRelation(){ relation = new ArrayList<NewspaperOffice>(); } public void registerNewspaperOffice(NewspaperOffice no) { if(!relation.contains(no)){ relation.add(no); } } public void removeNewspaperOffice(NewspaperOffice no) { if(relation.contains(no)){ relation.remove(no); } } public void notifyNewspaperOffice(String msg) { if(null != relation && relation.size()>0){ for(NewspaperOffice no : relation){ no.update(msg); } } } }
public abstract class NewspaperOffice { protected Relation relation; protected String newMsg; public void register(){ relation.registerNewspaperOffice(this); } public void remove(){ relation.removeNewspaperOffice(this); } public abstract void update(String newMsg); }
public class PeopleDaily extends NewspaperOffice{ public PeopleDaily(Relation relation){ this.relation = relation; } public void update(String newMsg) { this.newMsg = newMsg; System.out.println("People Daily got "+newMsg); } }