观察者模式提供给了OO程序设计者一种绝佳的获取对象更新的思路、这种思路使删除和增加被通知用户变得更灵活、更动态(在运行时决定哪些被通知用户被通知更新)。
在观察者模式的设计中,对象分为:观察者(Observer)和可观察者(Observable)。
观察者,顾名思义,该对象的角色行为是获得可观察者的更新信息,而可观察者的角色行为就是自身获得更新后将消息传递出去。
传统的方法是对象获得更新后要先寻找所要通知的其他对象(即new出所有通知对象的实例)然后调用被通知方的方法,将更新注入进被通知方,以“股票综合走势app”为例,该app连接“远程数据中心”,远程数据中心获得股市基本信息更新(例如:大盘指数,单支股票交易价,上涨个股数量,下跌个股数量),然后将更新的信息发送给每个用户的“app客户端”,app客户端再根据用户的个性化需求,将数据显示。现在有三个用户:Jack James 。jack抄底补仓,所以他想要了解下跌股票所占比例,James想要高位卖出,所以他想了解上涨股票所占比例,其代码实现往往是这样的:
class RemoteDataCenter{ //远程数据中心
float marketIdex ;
int riseShareNum ;
int declineShareNum;
float[] prices = new float[1000]; //假设有1000支股票
AppOfJack jack = new AppOfJack();
AppOfJames james = new AppOfJames();
public void notifyThem(){
jack.update(marketIdex, declineShareNum, prices);
james.update(marketIdex, riseShareNum, prices);
}
}
class AppOfJack{
float marketIdex ;
int riseShareNum ;
int declineShareNum;
float[] prices = new float[1000];
public void update(float marketIdex ,int declineShareNum ,float[] prices){ //个性化的信息通知
this.prices = prices;
this.marketIdex = marketIdex ;
this.declineShareNum = declineShareNum;
}
public void display(){ //定制的股票信息显示
float scaleOfdeclineSharesInMarket = declineShareNum/prices.length;
System.out.println("Market Index : " +marketIdex+ "\nScale Of decline shares : " + scaleOfdeclineSharesInMarket);
}
}
class AppOfJames{
float marketIdex ;
int riseShareNum ;
int declineShareNum;
float[] prices = new float[1000];
public void update(float marketIdex ,int riseShareNum ,float[] prices){ //个性化的信息通知
this.prices = prices;
this.marketIdex = marketIdex ;
this.riseShareNum = riseShareNum;
}
public void display(){ //定制的股票信息显示
float scaleOfRiseSharesInMarket = riseShareNum/prices.length;
System.out.println("Market Index : " +marketIdex+ "\nScale Of rise shares : " + scaleOfRiseSharesInMarket);
}
}
我们已经看到了这种传统的方式有大量的冗余代码,并且诸多内容都写死在程序中,几乎没有灵活性可言,比如:现在有一个用户Shely想要了解全部大盘信息,于是weomen又要增加一个新类,而且要修改原有代码:
class AppOfShely{
float marketIdex ;
int riseShareNum ;
int declineShareNum;
float[] prices = new float[1000];
public void update(float marketIdex ,int riseShareNum ,int declineShareNum ,float[] prices){ //新的推送通知内容
this.prices = prices;
this.marketIdex = marketIdex ;
this.declineShareNum = declineShareNum;
this.riseShareNum = riseShareNum;
}
public void display(){ //新的展示方式
int scaleOfRiseSharesInMarket = riseShareNum/prices.length;
float scaleOfdeclineSharesInMarket = declineShareNum/prices.length;
System.out.println("Market Index : " +marketIdex+ "\nScale Of rise shares : " + scaleOfRiseSharesInMarket + "\nScale Of decline shares : " +scaleOfdeclineSharesInMarket);
}
}
class RemoteDataCenter{ //重新修改的数据中心
float marketIdex ;
int riseShareNum ;
int declineShareNum;
float[] prices = new float[1000]; //假设有1000支股票
AppOfJack jack = new AppOfJack();
AppOfJames james = new AppOfJames();
AppOfShely shely = new AppOfShely();
public void notifyThem(){
jack.update(marketIdex, declineShareNum, prices);
james.update(marketIdex, riseShareNum, prices);
shely.update(marketIdex, riseShareNum, declineShareNum, prices);
}
}
从代码可以看出,这种传统的方式打破了诸多OO程序设计原则:
一、对扩展开放,对修改关闭;
二、针对接口编程,不针对具体实现编程;
三、封装变化;
当然了,如果这个系统的业务更复杂一些的话,估计必然会继续打破“多用组合,少用继承”和“尽量降低代码耦合程度”的原则。这种传统的设计方式使得后期系统升级维护变得极为困难。重新利用观察者模式来设计系统,代码实现如下:
interface Observer{
public void update(Obersvable e); //针对接口编程,不针对实现编程
public void display();
}
class AppOfJack implements Observer{
float marketIdex ;
int declineShareNum;
float[] prices = new float[1000];
public void update(Obersvable e){ //针对接口编程,不针对实现编程,这样可以让我们有更多的选择权
this.prices = e.getPrices();
this.marketIdex = e.getMarketIdex() ;
this.declineShareNum = e.getDeclineShareNum();
}
public void display(){
float scaleOfdeclineSharesInMarket = declineShareNum/prices.length;
System.out.println("Market Index : " +marketIdex+ "\nScale Of decline shares : " + scaleOfdeclineSharesInMarket);
}
}
class AppOfJames implements Observer{
float marketIdex ;
int riseShareNum ;
float[] prices = new float[1000];
public void update(Obersvable e){
this.prices = e.getPrices();
this.marketIdex = e.getMarketIdex() ;
this.riseShareNum = e.getRiseShareNum();
}
public void display(){
float scaleOfRiseSharesInMarket = riseShareNum/prices.length;
System.out.println("Market Index : " +marketIdex+ "\nScale Of rise shares : " + scaleOfRiseSharesInMarket);
}
}
class AppOfShely implements Observer{
float marketIdex ;
int riseShareNum ;
int declineShareNum;
float[] prices = new float[1000];
public void update(Obersvable e){
this.prices = e.getPrices();
this.marketIdex = e.getMarketIdex() ;
this.riseShareNum = e.getRiseShareNum();
this.declineShareNum = e.getDeclineShareNum();
}
public void display(){
int scaleOfRiseSharesInMarket = riseShareNum/prices.length;
float scaleOfdeclineSharesInMarket = declineShareNum/prices.length;
System.out.println("Market Index : " +marketIdex+ "\nScale Of rise shares : " + scaleOfRiseSharesInMarket + "\nScale Of decline shares : " + scaleOfdeclineSharesInMarket);
}
}
abstract class Obersvable{ //将变化的部分与不变的部分分离
protected float marketIdex ;
protected int riseShareNum ;
protected int declineShareNum;
protected float[] prices = new float[1000]; //假设有1000支股票
protected List<Observer> observers = new ArrayList<Observer>();
public float getMarketIdex() {
return marketIdex;
}
public int getRiseShareNum() {
return riseShareNum;
}
public int getDeclineShareNum() {
return declineShareNum;
}
public float[] getPrices() {
return prices;
}
public void regist(Observer e){ //可观察者将这样的接口暴露给观察者,可以使观察者自由地根据自己的需要决定是否在可观察者这里注册获取通知
observers.add(e);
}
public void delete(Observer e){ //可观察者将这样的接口暴露给观察者,可以使观察者自由地根据自己的需要决定是否不接受可观察者的通知
observers.remove(e);
}
public abstract void notifythem();
}
class RemoteDataCenter extends Obersvable{
public RemoteDataCenter(float marketIdex, int riseShareNum,
int declineShareNum, float[] prices) {
super();
this.marketIdex = marketIdex;
this.riseShareNum = riseShareNum;
this.declineShareNum = declineShareNum;
this.prices = prices;
}
public void notifythem(){
for(Observer e : observers){
e.update(this); //我们把对象传递给观察者,使得该观察者可以自由地抽取自己想要获得的更新,而不是将特定的数据直接传递给观察者
}
}
//我们不关心Observable对象是如何数据更新的,在这里我们只讨论其数据是如何通知的
}
观察者模式(Observer)为我们提供一种自由增加消息推送对象的方法,但是能理解这种设计模式的前提是建立正确的OO程序设计观:
一、将纷繁复杂的各式代码封装成类,并于现实中的对象建立思维领域的映射,为了是封装的类与现实中的对象更加贴切,要尽量从具体向抽象过度;
二、每当编写一个类时,就要当作是在刻画一个对象,一切该对象所蕴含的代码都要从该对象角度出发,即:把自己想象成这个对象,将其他对象想象成与自己平等的对象
三、由于其他对象与OO设计者所代表的对象是平等,所以,其他对象的属性对于OO设计者是透明的。