幾種經常使用的設計模式介紹html
最先提出「設計模式」概念的是建築設計大師亞力山大Alexander。在1970年他的《建築的永恆之道》裏描述了投計模式的發現,由於它已經存在了千百年之久,而現代才被經過大量的研究而被發現。java
在《建築的永恆之道》裏這樣描述:模式是一條由三個部分組成的通用規則:它表示了一個特定環境、一類問題和一個解決方案之間的關係。每個模式描述了一個不斷重複發生的問題,以及該問題解決方案的核心設計。mysql
在他的另外一本書《建築模式語言》中提到了如今已經定義了253種模式。好比:算法
說明城市主要的結構:亞文化區的鑲嵌、分散的工做點、城市的魅力、地方交通區spring
住宅團組:戶型混合、公共性的程度、住宅團組、聯排式住宅、丘狀住宅、老人天地室內環境和室外環境、陰和陽老是一鼓作氣sql
針對住宅:夫妻的領域、兒童的領域、朝東的臥室、農家的廚房、私家的沿街露臺、我的居室、起居空間的序列、多牀臥室、浴室、大儲藏室數據庫
針對辦公室、車間和公共建築物:靈活辦公空間、共同進餐、共同小組、賓至如歸、等候場所、小會議室、半私密辦公室編程
儘管亞力山大的著做是針對建築領域的,但他的觀點實際上適用於全部的工程設計領域,其中也包括軟件設計領域。「軟件設計模式」,這個術語是在1990年代由Erich Gamma等人從建築設計領域引入到計算機科學中來的。目前主要有23種。設計模式
建立對象時,再也不由咱們直接實例化對象;而是根據特定場景,由程序來肯定建立對象的方式,從而保證更大的性能、更好的架構優點。建立型模式主要有簡單工廠模式(並非23種設計模式之一)、工廠方法、抽象工廠模式、單例模式、生成器模式和原型模式。數組
用於幫助將多個對象組織成更大的結構。結構型模式主要有適配器模式adapter、橋接模式bridge、組合器模式component、裝飾器模式decorator、門面模式、亨元模式flyweight和代理模式proxy。
用於幫助系統間各對象的通訊,以及如何控制複雜系統中流程。行爲型模式主要有命令模式command、解釋器模式、迭代器模式、中介者模式、備忘錄模式、觀察者模式、狀態模式state、策略模式、模板模式和訪問者模式。
有些時候,容許自由建立某個類的實例沒有意義,還可能形成系統性能降低。若是一個類始終只能建立一個實例,則這個類被稱爲單例類,這種模式就被稱爲單例模式。
通常建議單例模式的方法命名爲:getInstance(),這個方法的返回類型確定是單例類的類型了。getInstance方法能夠有參數,這些參數多是建立類實例所須要的參數,固然,大多數狀況下是不須要的
publicclass Singleton {
publicstaticvoid main(String[] args) { //建立Singleton對象不能經過構造器,只能經過getInstance方法 Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); //將輸出true System.out.println(s1 == s2); }
//使用一個變量來緩存曾經建立的實例 privatestatic Singleton instance; //將構造器使用private修飾,隱藏該構造器 private Singleton(){ System.out.println("Singleton被構造!"); }
//提供一個靜態方法,用於返回Singleton實例 //該方法能夠加入自定義的控制,保證只產生一個Singleton對象 publicstatic Singleton getInstance() { //若是instance爲null,代表還未曾建立Singleton對象 //若是instance不爲null,則代表已經建立了Singleton對象,將不會執行該方法 if (instance == null) { //建立一個Singleton對象,並將其緩存起來 instance = new Singleton(); } returninstance; } } |
單例模式主要有以下兩個優點:
1) 減小建立Java實例所帶來的系統開銷
2) 便於系統跟蹤單個Java實例的生命週期、實例狀態等。
簡單工廠模式是由一個工廠對象決定建立出哪種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,能夠理解爲是不一樣工廠模式的一個特殊實現。
A實例調用B實例的方法,稱爲A依賴於B。若是使用new關鍵字來建立一個B實例(硬編碼耦合),而後調用B實例的方法。一旦系統須要重構:須要使用C類來代替B類時,程序不得不改寫A類代碼。而用工廠模式則不須要關心B對象的實現、建立過程。
Output,接口
publicinterface Output { //接口裏定義的屬性只能是常量 intMAX_CACHE_LINE = 50; //接口裏定義的只能是public的抽象實例方法 void out(); void getData(String msg); } |
Printer,Output的一個實現
//讓Printer類實現Output publicclass Printer implements Output { private String[] printData = new String[MAX_CACHE_LINE]; //用以記錄當前需打印的做業數 privateintdataNum = 0; publicvoid out() { //只要還有做業,繼續打印 while(dataNum > 0) { System.out.println("打印機打印:" + printData[0]); //把做業隊列總體前移一位,並將剩下的做業數減1 System.arraycopy(printData , 1, printData, 0, --dataNum); } } publicvoid getData(String msg) { if (dataNum >= MAX_CACHE_LINE) { System.out.println("輸出隊列已滿,添加失敗"); } else { //把打印數據添加到隊列裏,已保存數據的數量加1。 printData[dataNum++] = msg; } } } |
BetterPrinter,Output的一個實現
publicclass BetterPrinter implements Output { private String[] printData = new String[MAX_CACHE_LINE * 2]; //用以記錄當前需打印的做業數 privateintdataNum = 0; publicvoid out() { //只要還有做業,繼續打印 while(dataNum > 0) { System.out.println("高速打印機正在打印:" + printData[0]); //把做業隊列總體前移一位,並將剩下的做業數減1 System.arraycopy(printData , 1, printData, 0, --dataNum); } } publicvoid getData(String msg) { if (dataNum >= MAX_CACHE_LINE * 2) { System.out.println("輸出隊列已滿,添加失敗"); } else { //把打印數據添加到隊列裏,已保存數據的數量加1。 printData[dataNum++] = msg; } } } |
OutputFactory,簡單工廠類
public Output getPrinterOutput(String type) { if (type.equalsIgnoreCase("better")) { returnnew BetterPrinter(); } else { returnnew Printer(); } } |
Computer
publicclass Computer { private Output out;
public Computer(Output out) { this.out = out; } //定義一個模擬獲取字符串輸入的方法 publicvoid keyIn(String msg) { out.getData(msg); } //定義一個模擬打印的方法 publicvoid print() { out.out(); } publicstaticvoid main(String[] args) { //建立OutputFactory OutputFactory of = new OutputFactory(); //將Output對象傳入,建立Computer對象 Computer c = new Computer(of.getPrinterOutput("normal")); c.keyIn("建築永恆之道"); c.keyIn("建築模式語言"); c.print();
c = new Computer(of.getPrinterOutput("better")); c.keyIn("建築永恆之道"); c.keyIn("建築模式語言"); c.print(); } |
使用簡單工廠模式的優點:讓對象的調用者和對象建立過程分離,當對象調用者須要對象時,直接向工廠請求便可。從而避免了對象的調用者與對象的實現類以硬編碼方式耦合,以提升系統的可維護性、可擴展性。工廠模式也有一個小小的缺陷:當產品修改時,工廠類也要作相應的修改。
若是咱們不想在工廠類中進行邏輯判斷,程序能夠爲不一樣產品類提供不一樣的工廠,不一樣的工廠類和產不一樣的產品。
當使用工廠方法設計模式時,對象調用者須要與具體的工廠類耦合,如:
//工廠類的定義1 publicclass BetterPrinterFactory implements OutputFactory { public Output getOutput() { //該工廠只負責產生BetterPrinter對象 returnnew BetterPrinter(); } } //工廠類的定義2 publicclass PrinterFactory implements OutputFactory { public Output getOutput() { //該工廠只負責產生Printer對象 returnnew Printer(); } } //工廠類的調用 //OutputFactory of = new BetterPrinterFactory(); OutputFactory of = new PrinterFactory(); Computer c = new Computer(of.getOutput()); |
使用簡單工廠類,須要在工廠類裏作邏輯判斷。而工廠類雖然不用在工廠類作判斷。可是帶來了另外一種耦合:客戶端代碼與不一樣的工廠類耦合。
爲了解決客戶端代碼與不一樣工廠類耦合的問題。在工廠類的基礎上再增長一個工廠類,該工廠類不製造具體的被調用對象,而是製造不一樣工廠對象。如:
//抽象工廠類的定義,在工廠類的基礎上再建一個工廠類 publicclass OutputFactoryFactory { //僅定義一個方法用於返回輸出設備。 publicstatic OutputFactory getOutputFactory( String type) { if (type.equalsIgnoreCase("better")) { returnnew BetterPrinterFactory(); } else { returnnew PrinterFactory(); } } }
//抽象工廠類的調用 OutputFactory of = OutputFactoryFactory.getOutputFactory("better"); Computer c = new Computer(of.getOutput()); |
代理模式是一種應用很是普遍的設計模式,當客戶端代碼須要調用某個對象時,客戶端實際上不關心是否準確獲得該對象,它只要一個能提供該功能的對象便可,此時咱們就可返回該對象的代理(Proxy)。
代理就是一個Java對象表明另外一個Java對象來採起行動。如:
publicclass ImageProxy implements Image { //組合一個image實例,做爲被代理的對象 private Image image; //使用抽象實體來初始化代理對象 public ImageProxy(Image image) { this.image = image; } /** * 重寫Image接口的show()方法 * 該方法用於控制對被代理對象的訪問, * 並根據須要負責建立和刪除被代理對象 */ publicvoid show() { //只有當真正須要調用image的show方法時才建立被代理對象 if (image == null) { image = new BigImage(); } image.show(); } } |
調用時,先不建立:
Image image = new ImageProxy(null); |
hibernate默認啓用延遲加載,當系統加載A實體時,A實體關聯的B實體並未被加載出來,A實體所關聯的B實體所有是代理對象——只有等到A實體真正須要訪問B實體時,系統纔會去數據庫裏抓取B實體所對應的記錄。
藉助於Java提供的Proxy和InvocationHandler,能夠實如今運行時生成動態代理的功能,而動態代理對象就能夠做爲目標對象使用,並且加強了目標對象的功能。如:
Panther
publicinterface Panther { //info方法聲明 publicvoid info(); //run方法聲明 publicvoid run(); } |
GunPanther
publicclass GunPanther implements Panther { //info方法實現,僅僅打印一個字符串 publicvoid info() { System.out.println("我是一隻獵豹!"); } //run方法實現,僅僅打印一個字符串 publicvoid run() { System.out.println("我奔跑迅速"); } } |
MyProxyFactory,建立代理對象
publicclass MyProxyFactory { //爲指定target生成動態代理對象 publicstatic Object getProxy(Object target) throws Exception { //建立一個MyInvokationHandler對象 MyInvokationHandler handler = new MyInvokationHandler(); //爲MyInvokationHandler設置target對象 handler.setTarget(target); //建立、並返回一個動態代理 return Proxy.newProxyInstance(target.getClass().getClassLoader() , target.getClass().getInterfaces(), handler); } } |
MyInvokationHandler,加強代理的功能
publicclass MyInvokationHandler implements InvocationHandler { //須要被代理的對象 private Object target; publicvoid setTarget(Object target) { this.target = target; } //執行動態代理對象的全部方法時,都會被替換成執行以下的invoke方法 public Object invoke(Object proxy, Method method, Object[] args) throws Exception { TxUtil tx = new TxUtil(); //執行TxUtil對象中的beginTx。 tx.beginTx(); //以target做爲主調來執行method方法 Object result = method.invoke(target , args); //執行TxUtil對象中的endTx。 tx.endTx(); return result; } } |
TxUtil
publicclass TxUtil { //第一個攔截器方法:模擬事務開始 publicvoid beginTx() { System.out.println("=====模擬開始事務====="); } //第二個攔截器方法:模擬事務結束 publicvoid endTx() { System.out.println("=====模擬結束事務====="); } } |
publicstaticvoid main(String[] args) throws Exception { //建立一個原始的GunDog對象,做爲target Panther target = new GunPanther(); //以指定的target來建立動態代理 Panther panther = (Panther)MyProxyFactory.getProxy(target); //調用代理對象的info()和run()方法 panther.info(); panther.run(); } |
spring所建立的AOP代理就是這種動態代理。可是Spring AOP更靈活。
某個方法須要完成某一個功能,完成這個功能的大部分步驟已經肯定了,但可能有少許具體步驟沒法肯定,必須等到執行該方法時才能夠肯定。(在某些編程語言如Ruby、Perl裏,容許傳入一個代碼塊做爲參數。但Jara暫時還不支持代碼塊做爲參數)。在Java中,傳入該方法的是一個對象,該對象一般是某個接口的匿名實現類的實例,該接口一般被稱爲命令接口,這種設計方式也被稱爲命令模式。
如:
Command
publicinterface Command { //接口裏定義的process方法用於封裝「處理行爲」 void process(int[] target); } |
ProcessArray
publicclass ProcessArray { //定義一個each()方法,用於處理數組, publicvoid each(int[] target , Command cmd) { cmd.process(target); } } |
TestCommand
publicclass TestCommand { publicstaticvoid main(String[] args) { ProcessArray pa = new ProcessArray(); int[] target = {3, -4, 6, 4}; //第一次處理數組,具體處理行爲取決於Command對象 pa.each(target , new Command() { //重寫process()方法,決定具體的處理行爲 publicvoid process(int[] target) { for (int tmp : target ) { System.out.println("迭代輸出目標數組的元素:" + tmp); } } }); System.out.println("------------------"); //第二次處理數組,具體處理行爲取決於Command對象 pa.each(target , new Command() { //重寫process方法,決定具體的處理行爲 publicvoid process(int[] target) { int sum = 0; for (int tmp : target ) { sum += tmp; } System.out.println("數組元素的總和是:" + sum); } }); } } |
HibernateTemplate使用了executeXxx()方法彌補了HibernateTemplate的不足,該方法須要接受一個HibernateCallback接口,該接口的代碼以下:
public interface HibernateCallback { Object doInHibernate(Session session); } |
策略模式用於封裝系列的算法,這些算法一般被封裝在一個被稱爲Context的類中,客戶端程序能夠自由選擇其中一種算法,或讓Context爲客戶端選擇一種最佳算法——使用策略模式的優點是爲了支持算法的自由切換。
DiscountStrategy,折扣方法接口
publicinterface DiscountStrategy { //定義一個用於計算打折價的方法 double getDiscount(double originPrice); } |
OldDiscount,舊書打折算法
publicclass OldDiscount implements DiscountStrategy { // 重寫getDiscount()方法,提供舊書打折算法 publicdouble getDiscount(double originPrice) { System.out.println("使用舊書折扣..."); return originPrice * 0.7; } } |
VipDiscount,VIP打折算法
//實現DiscountStrategy接口,實現對VIP打折的算法 publicclass VipDiscount implements DiscountStrategy { // 重寫getDiscount()方法,提供VIP打折算法 publicdouble getDiscount(double originPrice) { System.out.println("使用VIP折扣..."); return originPrice * 0.5; } } |
策略定義
publicclass DiscountContext { //組合一個DiscountStrategy對象 private DiscountStrategy strategy; //構造器,傳入一個DiscountStrategy對象 public DiscountContext(DiscountStrategy strategy) { this.strategy = strategy; } //根據實際所使用的DiscountStrategy對象獲得折扣價 publicdouble getDiscountPrice(double price) { //若是strategy爲null,系統自動選擇OldDiscount類 if (strategy == null) { strategy = new OldDiscount(); } returnthis.strategy.getDiscount(price); } //提供切換算法的方法 publicvoid setDiscount(DiscountStrategy strategy) { this.strategy = strategy; } } |
測試
publicstaticvoid main(String[] args) { //客戶端沒有選擇打折策略類 DiscountContext dc = new DiscountContext(null); double price1 = 79; //使用默認的打折策略 System.out.println("79元的書默認打折後的價格是:" + dc.getDiscountPrice(price1)); //客戶端選擇合適的VIP打折策略 dc.setDiscount(new VipDiscount()); double price2 = 89; //使用VIP打折獲得打折價格 System.out.println("89元的書對VIP用戶的價格是:" + dc.getDiscountPrice(price2)); } |
使用策略模式可讓客戶端代碼在不一樣的打折策略之間切換,但也有一個小小的遺憾:客戶端代碼須要和不一樣的策略耦合。爲了彌補這個不足,咱們能夠考慮使用配置文件來指定DiscountContext使用哪一種打折策略——這就完全分離客戶端代碼和具體打折策略類。
隨着系統的不斷改進和開發,它們會變得愈來愈複雜,系統會生成大量的類,這使得程序流程更難被理解。門面模式可爲這些類提供一個簡化的接口,從而簡化訪問這些類的複雜性。
門面模式(Facade)也被稱爲正面模式、外觀模式,這種模式用於將一組複雜的類包裝到一個簡單的外部接口中。
原來的方式
// 依次建立三個部門實例 Payment pay = new PaymentImpl(); Cook cook = new CookImpl(); Waiter waiter = new WaiterImpl(); // 依次調用三個部門實例的方法來實現用餐功能 String food = pay.pay(); food = cook.cook(food); waiter.serve(food); |
門面模式
publicclass Facade { // 定義被Facade封裝的三個部門 Payment pay; Cook cook; Waiter waiter;
// 構造器 public Facade() { this.pay = new PaymentImpl(); this.cook = new CookImpl(); this.waiter = new WaiterImpl(); }
publicvoid serveFood() { // 依次調用三個部門的方法,封裝成一個serveFood()方法 String food = pay.pay(); food = cook.cook(food); waiter.serve(food); } } |
門面模式調用
Facade f = new Facade(); f.serveFood(); |
因爲實際的須要,某個類具備兩個以上的維度變化,若是隻是使用繼承將沒法實現這種須要,或者使得設計變得至關臃腫。而橋接模式的作法是把變化部分抽象出來,使變化部分與主類分離開來,從而將多個的變化完全分離。最後提供一個管理類來組合不一樣維度上的變化,經過這種組合來知足業務的須要。
Peppery口味風格接口:
publicinterface Peppery { String style(); } |
口味之一
publicclass PepperySytle implements Peppery { //實現"辣味"風格的方法 public String style() { return"辣味很重,很過癮..."; } } |
口味之二
publicclass PlainStyle implements Peppery { //實現"不辣"風格的方法 public String style() { return"味道清淡,很養胃..."; } } |
口味的橋樑
publicabstractclass AbstractNoodle { //組合一個Peppery變量,用於將該維度的變化獨立出來 protected Peppery style; //每份Noodle必須組合一個Peppery對象 public AbstractNoodle(Peppery style) { this.style = style; } publicabstractvoid eat(); } |
材料之一,繼承口味
publicclass PorkyNoodle extends AbstractNoodle { public PorkyNoodle(Peppery style) { super(style); } //實現eat()抽象方法 publicvoid eat() { System.out.println("這是一碗稍嫌油膩的豬肉麪條。" + super.style.style()); } } |
材料之二,繼承口味
publicclass BeefMoodle extends AbstractNoodle { public BeefMoodle(Peppery style) { super(style); } //實現eat()抽象方法 publicvoid eat() { System.out.println("這是一碗美味的牛肉麪條。" + super.style.style()); } } |
主程序
publicclass Test { publicstaticvoid main(String[] args) { //下面將獲得「辣味」的牛肉麪 AbstractNoodle noodle1 = new BeefMoodle( new PepperySytle()); noodle1.eat(); //下面將獲得「不辣」的牛肉麪 AbstractNoodle noodle2 = new BeefMoodle( new PlainStyle()); noodle2.eat(); //下面將獲得「辣味」的豬肉面 AbstractNoodle noodle3 = new PorkyNoodle( new PepperySytle()); noodle3.eat(); //下面將獲得「不辣」的豬肉面 AbstractNoodle noodle4 = new PorkyNoodle( new PlainStyle()); noodle4.eat(); } } |
Java EE應用中常見的DAO模式正是橋接模式的應用。
實際上,一個設計優良的項目,自己就是設計模式最好的教科書,例如Spring框架,當你深刻閱讀其源代碼時,你會發現這個框架到處充滿了設計模式的應用場景。
http://www.cnblogs.com/liuling/archive/2013/04/20/observer.html
觀察者模式結構中包括四種角色:
1、主題:主題是一個接口,該接口規定了具體主題須要實現的方法,好比添加、刪除觀察者以及通知觀察者更新數據的方法。
2、觀察者:觀察者也是一個接口,該接口規定了具體觀察者用來更新數據的方法。
3、具體主題:具體主題是一個實現主題接口的類,該類包含了會常常發生變化的數據。並且還有一個集合,該集合存放的是觀察者的引用。
四:具體觀察者:具體觀察者是實現了觀察者接口的一個類。具體觀察者包含有能夠存放具體主題引用的主題接口變量,以便具體觀察者讓具體主題將本身的引用添加到具體主題的集合中,讓本身成爲它的觀察者,或者讓這個具體主題將本身從具體主題的集合中刪除,使本身不在時它的觀察者。
觀察者模式定義了對象間的一對多依賴關係,讓一個或多個觀察者對象觀察一個主題對象。當主題對象的狀態發生變化時,系統能通知全部的依賴於此對象的觀察者對象,從而使得觀察者對象可以自動更新。
在觀察者模式中,被觀察的對象經常也被稱爲目標或主題(Subject),依賴的對象被稱爲觀察者(Observer)。
Observer,觀察者接口:
觀察者:觀察者也是一個接口,該接口規定了具體觀察者用來更新數據的方法
publicinterface Observer { void update(Observable o, Object arg); } |
Observable,目標或主題:
主題:主題是一個接口,該接口規定了具體主題須要實現的方法,好比添加、刪除觀察者以及通知觀察者更新數據的方法
import java.util.ArrayList; import java.util.List; import java.util.Iterator;
publicabstractclass Observable { // 用一個List來保存該對象上全部綁定的事件監聽器 List<Observer> observers = new ArrayList<Observer>();
// 定義一個方法,用於從該主題上註冊觀察者 publicvoid registObserver(Observer o) { observers.add(o); }
// 定義一個方法,用於從該主題中刪除觀察者 publicvoid removeObserver(Observer o) { observers.add(o); }
// 通知該主題上註冊的全部觀察者 publicvoid notifyObservers(Object value) { // 遍歷註冊到該被觀察者上的全部觀察者 for (Iterator it = observers.iterator(); it.hasNext();) { Observer o = (Observer) it.next(); // 顯式每一個觀察者的update方法 o.update(this, value); } } } |
Product被觀察類:
具體主題:具體主題是一個實現主題接口的類,該類包含了會常常發生變化的數據。並且還有一個集合,該集合存放的是觀察者的引用。
publicclass Product extends Observable { // 定義兩個屬性 private String name; privatedoubleprice;
// 無參數的構造器 public Product() { }
public Product(String name, double price) { this.name = name; this.price = price; }
public String getName() { returnname; }
// 當程序調用name的setter方法來修改Product的name屬性時 // 程序天然觸發該對象上註冊的全部觀察者 publicvoid setName(String name) { this.name = name; notifyObservers(name); }
publicdouble getPrice() { returnprice; }
// 當程序調用price的setter方法來修改Product的price屬性時 // 程序天然觸發該對象上註冊的全部觀察者 publicvoid setPrice(double price) { this.price = price; notifyObservers(price); } } |
具體觀察者:具體觀察者是實現了觀察者接口的一個類。具體觀察者包含有能夠存放具體主題引用的主題接口變量,以便具體觀察者讓具體主題將本身的引用添加到具體主題的集合中,讓本身成爲它的觀察者,或者讓這個具體主題將本身從具體主題的集合中刪除,使本身不在時它的觀察者。
NameObserver名稱觀察者:
import javax.swing.JFrame; import javax.swing.JLabel;
publicclass NameObserver implements Observer { // 實現觀察者必須實現的update方法 publicvoid update(Observable o, Object arg) { if (arg instanceof String) { // 產品名稱改變值在name中 String name = (String) arg; // 啓動一個JFrame窗口來顯示被觀察對象的狀態改變 JFrame f = new JFrame("觀察者"); JLabel l = new JLabel("名稱改變爲:" + name); f.add(l); f.pack(); f.setVisible(true); System.out.println("名稱觀察者:" + o + "物品名稱已經改變爲: " + name); } } } |
PriceObserver價格觀察者:
publicclass PriceObserver implements Observer { // 實現觀察者必須實現的update方法 publicvoid update(Observable o, Object arg) { if (arg instanceof Double) { System.out.println("價格觀察者:" + o + "物品價格已經改變爲: " + arg); } } } |
測試:
publicclass Test { publicstaticvoid main(String[] args) { // 建立一個被觀察者對象 Product p = new Product("電視機", 176); // 建立兩個觀察者對象 NameObserver no = new NameObserver(); PriceObserver po = new PriceObserver(); // 向被觀察對象上註冊兩個觀察者對象 p.registObserver(no); p.registObserver(po); // 程序調用setter方法來改變Product的name和price屬性 p.setName("書桌"); p.setPrice(345f); } } |
其中Java工具類提供了被觀察者抽象基類:java.util.Observable。觀察者接口:java.util.Observer。
咱們能夠把觀察者接口理解成事件監聽接口,而被觀察者對象也可當成事件源處理——換個角度來思考:監聽,觀察,這兩個詞語之間有本質的區別嗎?Java事件機制的底層實現,自己就是經過觀察者模式來實現的。除此以外,主題/訂閱模式下的JMS自己就是觀察者模式的應用。