在軟件開發中,咱們常常須要向某些對象發送請求(調用其中的某個或某些方法),可是並不知道請求的接收者是誰,也不知道被請求的操做是哪一個,此時,咱們特別但願可以以一種鬆耦合的方式來設計軟件,使得請求發送者與請求接收者可以消除彼此之間的耦合,讓對象之間的調用關係更加靈活,能夠靈活地指定請求接收者以及被請求的操做。命令模式爲此類問題提供了一個較爲完美的解決方案。命令模式能夠將請求發送者和接收者徹底解耦,發送者與接收者之間沒有直接引用關係,發送請求的對象只須要知道如何發送請求,而沒必要知道如何完成請求。命令模式定義以下:java
命令模式(Command Pattern):將一個請求封裝爲一個對象,從而讓咱們可用不一樣的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操做。命令模式是一種對象行爲型模式,其別名爲動做(Action)模式或事務(Transaction)模式。算法
命令模式的定義比較複雜,提到了不少術語,例如「用不一樣的請求對客戶進行參數化」、「對請求排隊」、「記錄請求日誌」、「支持可撤銷操做」等,在後面咱們將對這些術語進行一一講解。編程
命令模式的核心在於引入了命令類,經過命令類來下降發送者和接收者的耦合度,請求發送者只需指定一個命令對象,再經過命令對象來調用請求接收者的處理方法,其結構如圖所示:設計模式
Command(抽象命令類):抽象命令類通常是一個抽象類或接口,在其中聲明瞭用於執行請求的 execut e() 等方法,經過這些方法能夠調用請求接收者的相關操做。學習
ConcreteCommand(具體命令類):具體命令類是抽象命令類的子類,實現了在抽象命令類中聲明的方 法,它對應具體的接收者對象,將接收者對象的動做綁定其中。在實現 execute() 方法時,將調用接收者對 象的相關操做(Action)。ui
Invoker(調用者):調用者即請求發送者,它經過命令對象來執行請求。一個調用者並不須要在設計時肯定 其接收者,所以它只與抽象命令類之間存在關聯關係。在程序運行時能夠將一個具體命令對象注入其中,再 調用具體命令對象的 execute() 方法,從而實現間接調用請求接收者的相關操做。this
Receiver(接收者):接收者執行與請求相關的操做,它具體實現對請求的業務處理。spa
命令模式的本質是對請求進行封裝,一個請求對應於一個命令,將發出命令的責任和執行命令的責任分割開。每 一個命令都是一個操做:請求的一方發出請求要求執行一個操做;接收的一方收到請求,並執行相應的操做。命令模式容許請求的一方和接收的一方獨立開來,使得請求的一方沒必要知道接收請求的一方的接口,更沒必要知道請求如何被接收、操做是否被執行、什麼時候被執行,以及是怎麼被執行的。設計
命令模式的關鍵在於引入了抽象命令類,請求發送者針對抽象命令類編程,只有實現了抽象命令類的具體命令才 與請求接收者相關聯。在最簡單的抽象命令類中只包含了一個抽象的execute() 方法,每一個具體命令類將一個Receiver 類型的對象做爲一個實例變量進行存儲,從而具體指定一個請求的接收者,不一樣的具體命令類提供了 execute() 方法的不一樣實現,並調用不一樣接收者的請求處理方法。 日誌
public class A { //功能鍵設置窗口類 class FBSettingWindow { private String title; //窗口標題 //定義一個ArrayList來存儲全部功能鍵 private ArrayList<FunctionButton> functionButtons = new ArrayList<FunctionButton>(); public void addFunctionButton(FunctionButton fb) { functionButtons.add(fb); } public void removeFunctionButton(FunctionButton fb) { functionButtons.remove(fb); } //顯示窗口及功能鍵 public void display() { System.out.println("顯示窗口:" + this.title); System.out.println("顯示功能鍵:"); for (Object obj : functionButtons) { System.out.println(((FunctionButton) obj).getName()); } System.out.println("------------------------------"); } } //功能鍵類:請求發送者 class FunctionButton { private String name; //功能鍵名稱 private Command command; //維持一個抽象命令對象的引用 public FunctionButton(String name) { this.name = name; } public String getName() { return this.name; } //爲功能鍵注入命令 public void setCommand(Command command) { this.command = command; } //發送請求的方法 public void onClick() { System.out.print("點擊功能鍵:"); command.execute(); } } //抽象命令類 abstract class Command { public abstract void execute(); } //幫助命令類:具體命令類 class HelpCommand extends Command { private HelpHandler hhObj; //維持對請求接收者的引用 public HelpCommand() { hhObj = new HelpHandler(); } //命令執行方法,將調用請求接收者的業務方法 public void execute() { hhObj.display(); } } //最小化命令類:具體命令類 class MinimizeCommand extends Command { private WindowHanlder whObj; //維持對請求接收者的引用 public MinimizeCommand() { whObj = new WindowHanlder(); } //命令執行方法,將調用請求接收者的業務方法 public void execute() { whObj.minimize(); } } //窗口處理類:請求接收者 class WindowHanlder { public void minimize() { System.out.println("將窗口最小化至托盤!"); } } //幫助文檔處理類:請求接收者 class HelpHandler { public void display() { System.out.println("顯示幫助文檔!"); } } } // 客戶端調用 class Client { public static void main(String args[]) { FBSettingWindow fbsw = new FBSettingWindow("功能鍵設置"); FunctionButton fb1, fb2; fb1 = new FunctionButton("功能鍵1"); fb2 = new FunctionButton("功能鍵1"); Command command1, command2; //經過讀取配置文件和反射生成具體命令對象 command1 = (Command) XMLUtil.getBean(0); command2 = (Command) XMLUtil.getBean(1); //將命令對象注入功能鍵 fb1.setCommand(command1); fb2.setCommand(command2); fbsw.addFunctionButton(fb1); fbsw.addFunctionButton(fb2); fbsw.display(); //調用功能鍵的業務方法 fb1.onClick(); fb2.onClick(); } }
若是須要修改功能鍵的功能,例如某個功能鍵能夠實現「自動截屏」,只須要對應增長一個新的具體命令類,在該命令類與屏幕處理者(ScreenHandler)之間建立一個關聯關係,而後將該具體命令類的對象經過配置文件注 入到某個功能鍵便可,原有代碼無須修改,符合「開閉原則」。在此過程當中,每個具體命令類對應一個請求的 處理者(接收者),經過向請求發送者注入不一樣的具體命令對象可使得相同的發送者對應不一樣的接收者,從而 實現「將一個請求封裝爲一個對象,用不一樣的請求對客戶進行參數化」,客戶端只須要將具體命令對象做爲參數 注入請求發送者,無須直接操做請求的接收者。
(1) 下降系統的耦合度。因爲請求者與接收者之間不存在直接引用,所以請求者與接收者之間實現徹底解耦,相同 的請求者能夠對應不一樣的接收者,一樣,相同的接收者也能夠供不一樣的請求者使用,二者之間具備良好的獨立 性。
(2) 新的命令能夠很容易地加入到系統中。因爲增長新的具體命令類不會影響到其餘類,所以增長新的具體命令類 很容易,無須修改原有系統源代碼,甚至客戶類代碼,知足「開閉原則」的要求。
(3) 能夠比較容易地設計一個命令隊列或宏命令(組合命令)。
(4) 爲請求的撤銷(Undo)和恢復(Redo)操做提供了一種設計和實現方案。
使用命令模式可能會致使某些系統有過多的具體命令類。由於針對每個對請求接收者的調用操做都須要設計一 個具體命令類,所以在某些系統中可能須要提供大量的具體命令類,這將影響命令模式的使用。
(1) 系統須要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互。請求調用者無須知道接收者的存在,也無須知道接收者是誰,接收者也無須關心什麼時候被調用。
(2) 系統須要在不一樣的時間指定請求、將請求排隊和執行請求。一個命令對象和請求的初始調用者能夠有不一樣的生 命期,換言之,最初的請求發出者可能已經不在了,而命令對象自己仍然是活動的,能夠經過該命令對象去調用 請求接收者,而無須關心請求調用者的存在性,能夠經過請求日誌文件等機制來具體實現。
(3) 系統須要支持命令的撤銷(Undo)操做和恢復(Redo)操做。
(4) 系統須要將一組操做組合在一塊兒造成宏命令。
迭代器模式(Iterator Pattern):提供一種方法來訪問聚合對象,而不用暴露這個對象的內部表示,其別名爲遊標(Cursor)。迭代器模式是一種對象行爲型模式。
public class A { interface Iterator { void first(); //將遊標指向第一個元素 void next(); //將遊標指向下一個元素 boolean hasNext(); //判斷是否存在下一個元素 Object currentItem(); //獲取遊標指向的當前元素 } class ConcreteIterator implements Iterator { private ConcreteAggregate objects; //維持一個對具體聚合對象的引用,以便於訪問存儲在聚合對象中的數據 private int cursor; //定義一個遊標,用於記錄當前訪問位置 public ConcreteIterator(ConcreteAggregate objects) { this.objects = objects; } public void first() { ......} public void next() { ......} public boolean hasNext() { ......} public Object currentItem() { ......} } // 聚合類 interface Aggregate { Iterator createIterator(); } // 具體聚合類是抽象聚合類的子類,一方面負責存儲數據, // 另外一方面實現了在抽象聚合類中聲明的工廠方法 createIterator(),用於返回一個與該具體聚合類對應的具體迭代器對象 class ConcreteAggregate implements Aggregate { ...... public Iterator createIterator() { return new ConcreteIterator(this); } ...... } }
(1) 它支持以不一樣的方式遍歷一個聚合對象,在同一個聚合對象上能夠定義多種遍歷方式。在迭代器模式中只須要用一個不一樣的迭代器來替換原有迭代器便可改變遍歷算法,咱們也能夠本身定義迭代器的子類以支持新的遍歷方式。
(2) 迭代器簡化了聚合類。因爲引入了迭代器,在原有的聚合對象中不須要再自行提供數據遍歷等方法,這樣能夠簡化聚合類的設計。
(3) 在迭代器模式中,因爲引入了抽象層,增長新的聚合類和迭代器類都很方便,無須修改原有代碼,知足「開閉原則」的要求。
(1) 因爲迭代器模式將存儲數據和遍歷數據的職責分離,增長新的聚合類須要對應增長新的迭代器類,類的個數成 對增長,這在必定程度上增長了系統的複雜性。
(2) 抽象迭代器的設計難度較大,須要充分考慮到系統未來的擴展,例如 JDK 內置迭代器 Iterator 就沒法實現逆向遍歷,若是須要實現逆向遍歷,只能經過其子類 ListIterator 等來實現,而 ListIterator 迭代器沒法用於操做 Set 類型的聚合對象。在自定義迭代器時,建立一個考慮全面的抽象迭代器並非件很容易的事情。
(1) 訪問一個聚合對象的內容而無須暴露它的內部表示。將聚合對象的訪問與內部數據的存儲分離,使得訪問聚合 對象時無須瞭解其內部實現細節。
(2) 須要爲一個聚合對象提供多種遍歷方式。
(3) 爲遍歷不一樣的聚合結構提供一個統一的接口,在該接口的實現類中爲不一樣的聚合結構提供不一樣的遍歷方式,而客戶端能夠一致性地操做該接口。
觀察者模式是使用頻率最高的設計模式之一,它用於創建一種對象與對象之間的依賴關係,一個對象發生改變時將自動通知其餘對象,其餘對象將相應做出反應。在觀察者模式中,發生改變的對象稱爲觀察目標,而被通知的對象稱爲觀察者,一個觀察目標能夠對應多個觀察者,並且這些觀察者之間能夠沒有任何相互聯繫,能夠根據須要增長和刪除觀察者,使得系統更易於擴展。
觀察者模式定義以下:
觀察者模式(Observer Pattern):定義對象之間的一種一對多依賴關係,使得每當一個對象狀態發生改變時,其相關依賴對象皆獲得通知並被自動更新。觀察者模式的別名包括發佈-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模 式。觀察者模式是一種對象行爲型模式。
觀察者模式結構中一般包括觀察目標和觀察者兩個繼承層次結構,其結構如圖所示:
觀察者模式描述瞭如何創建對象與對象之間的依賴關係,以及如何構造知足這種需求的系統。
觀察者模式與責任鏈模式都是在內部存的對象的引用,經過調用方法的方式達到通知的目的,可是責任鏈是鏈式調用,一次只能通知一個觀察者。
public class A { //抽象觀察類 interface Observer { public String getName(); public void setName(String name); public void help(); //聲明支援盟友方法 public void beAttacked(AllyControlCenter acc); //聲明遭受攻擊方法 } //戰隊成員類:具體觀察者類 class Player implements Observer { private String name; public Player(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } //支援盟友方法的實現 public void help() { System.out.println("堅持住," + this.name + "來救你!"); } //遭受攻擊方法的實現,當遭受攻擊時將調用戰隊控制中心類的通知方法notifyObserver()來通知盟友 public void beAttacked(AllyControlCenter acc) { System.out.println(this.name + "被攻擊!"); acc.notifyObserver(name); } } //戰隊控制中心類:目標類 abstract class AllyControlCenter { protected String allyName; //戰隊名稱 protected ArrayList<Observer> players = new ArrayList<Observer>(); //定義一個集合用於存儲戰隊成員 public void setAllyName(String allyName) { this.allyName = allyName; } public String getAllyName() { return this.allyName; } //註冊方法 public void join(Observer obs) { System.out.println(obs.getName() + "加入" + this.allyName + "戰隊!"); players.add(obs); } //註銷方法 public void quit(Observer obs) { System.out.println(obs.getName() + "退出" + this.allyName + "戰隊!"); players.remove(obs); } //聲明抽象通知方法 public abstract void notifyObserver(String name); } //具體戰隊控制中心類:具體目標類 class ConcreteAllyControlCenter extends AllyControlCenter { public ConcreteAllyControlCenter(String allyName) { System.out.println(allyName + "戰隊組建成功!"); System.out.println("----------------------------"); this.allyName = allyName; } //實現通知方法 public void notifyObserver(String name) { System.out.println(this.allyName + "戰隊緊急通知,盟友" + name + "遭受敵人攻擊!"); //遍歷觀察者集合,調用每個盟友(本身除外)的支援方法 for (Observer obs : players) { if (!obs.getName().equalsIgnoreCase(name)) { obs.help(); } } } } }
客戶端類
class Client { public static void main(String args[]) { //定義觀察目標對象 AllyControlCenter acc = new ConcreteAllyControlCenter("金庸羣俠"); //定義四個觀察者對象 Observer player1, player2, player3, player4; player1 = new Player("楊過"); acc.join(player1); player2 = new Player("令狐沖"); acc.join(player2); player3 = new Player("張無忌"); acc.join(player3); player4 = new Player("段譽"); acc.join(player4); //某成員遭受攻擊(通知其它成員) player1.beAttacked(acc); } }
觀察者模式在 Java 語言中的地位很是重要。在 JDK 的 java.util 包中,提供了 Observable 類以及 Observer 接口,它們構成了 JDK 對觀察者模式的支持。
(1) 觀察者模式能夠實現表示層和數據邏輯層的分離,定義了穩定的消息更新傳遞機制,並抽象了更新接口,使得能夠有各類各樣不一樣的表示層充當具體觀察者角色。
(2) 觀察者模式在觀察目標和觀察者之間創建一個抽象的耦合。觀察目標只須要維持一個抽象觀察者的集合,無須 瞭解其具體觀察者。因爲觀察目標和觀察者沒有緊密地耦合在一塊兒,所以它們能夠屬於不一樣的抽象化層次。
(3) 觀察者模式支持廣播通訊,觀察目標會向全部已註冊的觀察者對象發送通知,簡化了一對多系統設計的難度。
(4) 觀察者模式知足「開閉原則」的要求,增長新的具體觀察者無須修改原有系統代碼,在具體觀察者與觀察目標 之間不存在關聯關係的狀況下,增長新的觀察目標也很方便。
(1) 若是一個觀察目標對象有不少直接和間接觀察者,將全部的觀察者都通知到會花費不少時間。
(2) 若是在觀察者和觀察目標之間存在循環依賴,觀察目標會觸發它們之間進行循環調用,可能致使系統崩潰。
(3) 觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生 了變化。
(1) 一個抽象模型有兩個方面,其中一個方面依賴於另外一個方面,將這兩個方面封裝在獨立的對象中使它們能夠各 自獨立地改變和複用。
(2) 一個對象的改變將致使一個或多個其餘對象也發生改變,而並不知道具體有多少對象將發生改變,也不知道這 些對象是誰。
(3) 須要在系統中建立一個觸發鏈,A 對象的行爲將影響B對象,B 對象的行爲將影響 C 對象......,可使用觀察者模式建立一種鏈式觸發機制。
在底層的實現上差很少,觀察者模式內部存的是一個List<Observer>,責任鏈模式內部存的是this對象的引用
觀察者模式是爲了解決對象間的一種一對多依賴關係,使得每當一個對象改變狀態,則其相關依賴對象皆獲得通知並被自動更新。(不必定是同一個東西,只是一個觸發器)
責任鏈模式是爲了解決多個處理耦合在一塊兒(爲了單一原則),從而拆分紅多個類進行處理(處理同一個東西)
設計模式是圍繞着那6大原則設計的,最主要的是「開閉原則」,爲了方便大型系統的擴展,學習設計模式的時候必定要了解這些設計模式的存在是爲了解決什麼問題的。