設計模式:觀察者(Observer)模式
設計模式:觀察者(Observer)模式
一、前言
觀察者模式其實最好的名稱應該是“發布訂閱”模式,和我們現在大數據之中的發布訂閱方式比較類似,但是也有區別的地方,在上一個設計模式,我們學習的是仲裁者模式,其中當控件的狀態發生改變的時候就會向仲裁者發出信息,讓仲裁者進行仲裁,這其實和發布訂閱非常的類似,但是用處是不一樣的,仲裁者模式是用來解除復雜對象之間的相互調用的關系,從而獨立出來進行開發,而觀察者模式是在被觀察者狀態改變的時候被動的被喚醒進行相應的處理,兩者的實現比較類似,比如都是被動喚醒的,但是思想和用處是不一樣的,被喚醒之後的處理是不一樣的。
二、代碼
首先我們自己實現觀察者模式,其次我們使用java已經實現好的觀察者接口,然後來對比一下兩者的不同。
2.1、自己實現觀察者模式
NumberGenerator 類:
1 package zyr.dp.observer; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; 5 6 public abstract class NumberGenerator { 7 8 private ArrayList observers=new ArrayList(); 9 10 public void add(Observer observer){ 11 observers.add(observer);12 } 13 public void remove(Observer observer){ 14 observers.remove(observer); 15 } 16 public void notifyObserver(){ 17 Iterator it=observers.iterator(); 18 while(it.hasNext()){ 19 Observer object=(Observer)it.next(); 20 object.update(this); 21 } 22 } 23 public abstract void execuate(); 24 public abstract int getNumber(); 25 }
RandomNumberGenerator 類:
1 package zyr.dp.observer; 2 3 import java.util.Random; 4 5 public class RandomNumberGenerator extends NumberGenerator { 6 7 private Random random=new Random(); 8 private int number; 9 public int getNumber(){ 10 return number; 11 } 12 public void execuate() { 13 for(int i=0;i<20;i++){ 14 number=random.nextInt(60); 15 notifyObserver(); 16 } 17 } 18 19 }
Observer接口:
1 package zyr.dp.observer; 2 3 public interface Observer { 4 public abstract void update(NumberGenerator object); 5 }
DigitalObserver類:
1 package zyr.dp.observer; 2 3 public class DigitalObserver implements Observer { 4 5 public void update(NumberGenerator object) { 6 System.out.println("DigitalObserver:"+object.getNumber()); 7 try { 8 Thread.sleep(100); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 } 13 14 }
GraphObserver類:
1 package zyr.dp.observer; 2 3 public class GraphObserver implements Observer { 4 5 public void update(NumberGenerator object) { 6 System.out.print("GraphObserver:"); 7 for(int i=0;i<object.getNumber();i++){ 8 System.out.print("*"); 9 try { 10 Thread.sleep(100); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 System.out.println(); 16 } 17 18 }
Main類:
1 package zyr.dp.observer; 2 3 public class Main { 4 5 public static void main(String[] args) { 6 NumberGenerator numberGenerator=new RandomNumberGenerator(); 7 numberGenerator.add(new DigitalObserver()); 8 numberGenerator.add(new GraphObserver()); 9 10 numberGenerator.execuate(); 11 } 12 13 }
運行結果:
DigitalObserver:20 GraphObserver:******************** DigitalObserver:56 GraphObserver:******************************************************** DigitalObserver:11 GraphObserver:*********** DigitalObserver:52 GraphObserver:**************************************************** DigitalObserver:54 GraphObserver:****************************************************** DigitalObserver:41 GraphObserver:***************************************** DigitalObserver:39 GraphObserver:*************************************** DigitalObserver:14 GraphObserver:************** DigitalObserver:18 GraphObserver:****************** DigitalObserver:35 GraphObserver:*********************************** DigitalObserver:40 GraphObserver:**************************************** DigitalObserver:3 GraphObserver:*** DigitalObserver:0 GraphObserver: DigitalObserver:43 GraphObserver:******************************************* DigitalObserver:29 GraphObserver:***************************** DigitalObserver:2 GraphObserver:** DigitalObserver:48 GraphObserver:************************************************ DigitalObserver:0 GraphObserver: DigitalObserver:48 GraphObserver:************************************************ DigitalObserver:40 GraphObserver:****************************************運行結果
由此可以看到當被觀察者的狀態發生改變的時候會主動通知觀察者,使用notifyObserver的方法將自己的狀態傳遞過去,因為是自己定義的被觀察者的抽象類以及接口,因此使用起來非常的方便。代碼也不是很多,能夠按照自己的要求來完成更新操作,對比於仲裁者模式,被觀察者是主動將自己的內容傳遞給觀察者的,而仲裁者模式中,組員是本身就已經組合(委托)進了仲裁者之中,這也是一點不同。代碼比較簡單,這裏被觀察者使用了抽象類而不使用接口的原因是需要定義對觀察者對象的委托,因此使用了抽象類,而觀察者只用了update方法將被觀察者通過參數傳遞的方式委托進來,因此使用接口更加清晰一點,當然抽象類也可以,只不過能使用接口的就不要使用抽象類,因為一個類只能繼承一個父類,但是可以實現很多接口。
2.2、使用java自帶的觀察者模式
RandomNumberGenerator 類:
1 package zyr.dp.java; 2 3 import java.util.Observable; 4 import java.util.Random; 5 6 public class RandomNumberGenerator extends Observable { 7 8 private Random random=new Random(); 9 private int number; 10 public int getNumber(){ 11 return number; 12 } 13 public void execuate() { 14 for(int i=0;i<20;i++){ 15 number=random.nextInt(60); 16 setChanged(); 17 notifyObservers(); 18 } 19 } 20 21 }
DigitalObserver類:
1 package zyr.dp.java; 2 3 import java.util.Observable; 4 import java.util.Observer; 5 6 public class DigitalObserver implements Observer { 7 8 public void update(Observable object, Object arg) { 9 System.out.println("DigitalObserver為:"+((RandomNumberGenerator)object).getNumber()); 10 try { 11 Thread.sleep(100); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 } 16 17 }
GraphObserver 類:
1 package zyr.dp.java; 2 3 import java.util.Observable; 4 import java.util.Observer; 5 6 public class GraphObserver implements Observer { 7 8 9 public void update(Observable object, Object arg) { 10 System.out.print("GraphObserver為:"); 11 for(int i=0;i<((RandomNumberGenerator)object).getNumber();i++){ 12 System.out.print("*"); 13 try { 14 Thread.sleep(100); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 } 19 System.out.println(); 20 } 21 22 }
Main類:
1 package zyr.dp.java; 2 3 import java.util.Observable; 4 5 public class Main { 6 7 public static void main(String[] args) { 8 Observable observable=new RandomNumberGenerator(); 9 observable.addObserver(new DigitalObserver()); 10 observable.addObserver(new GraphObserver()); 11 12 ((RandomNumberGenerator)observable).execuate(); 13 } 14 15 }
可以看到在java自定義的觀察者模式之中,首先要修改 setChanged();來使得notifyObservers生效,其次,傳遞的參數不是很靈活,需要強制轉換成我們想要的東西,最後在使用的時候也需要強制轉換,這是比較麻煩的,並且被觀察者也是繼承了抽象類Observable,不方便以後功能的擴展,如果以後再想繼承其它的類就很困難了。我們自己設計的時候,可以使用某些方式把抽象類變成接口,不過也需要一定的操作。
三、總結
通過觀察者模式使得觀察者和被觀察者之間面向抽象編程,觀察者不用知道自己觀察的對象到底是誰的實例,只需要知道這個對象繼承了被觀察者的抽象類,因此當被觀察者增加的時候,觀察者可以不用修改。同樣的,對於被觀察者的實例來說,並不需要知道自己到底是被哪一個觀察者觀察了,只需要知道觀察自己的觀察者肯定使用了觀察者的接口,因此觀察者和被觀察者之間通過面向抽象編程提高了可擴展性,便於組件化。
我們可以看到在面向對象編程中能夠使用委托(組合)的就不要使用繼承,委托是弱關聯,繼承是強關聯。並且將一些共同操作抽象出來放到抽象類之中去定義,在參數傳遞中不使用具體類型而是用接口或者抽象類,這樣的設計思想便於組件化,具有可替換性。
設計模式:觀察者(Observer)模式