設計模式 --行為型
范圍\目的 |
創建型模式 |
結構型模式 |
行為型模式 |
類模式 |
工廠方法模式 |
(類)適配器模式 |
解釋器模式 模板方法模式 |
對象模式 |
抽象工廠模式 建造者模式 原型模式 單例模式 |
(對象)適配器模式 橋接模式 組合模式 裝飾模式 外觀模式 享元模式 代理模式 |
職責鏈模式 命令模式 迭代器模式 中介者模式 備忘錄模式 觀察者模式 狀態模式 策略模式 訪問者模式 |
根據目的、用途的不同,分為創建性模式、結構性模式、行為性模式。創建型模式主要用於創建對象,結構型模式主要用於處理類和對象的組合,行為性模式主要用於描述類或對象的交互以及職責分配。
根據處理范圍不同,設計模式又可分為類模式和對象模式,類模式處理類與子類的關系,通過處理這些關系來建立繼承,屬於靜態關系,在編譯時候確定下來;對象模式處理對象之間的關系,運行時發生變化,屬於動態關系。
3.行為性模式Behavioral(11種)
是在不同的對象之間來划分責任和算法的抽象形式,不僅僅是關於類和對象的,也是關於類和對象之間是如何作用的,同樣也分為類行為模式和對象行為模式,類行為模式,是使用繼承關系在幾個類之間分配它的功能;對象行為模式,是使用對象的聚合/組合來分配行為
一. 職責鏈模式(對象行為性模式)
定義:通過給多個對象處理請求的機會,減少請求的發送者與接收者之間的耦合。將接收對象鏈接起來,在鏈中傳遞請求,直到有一個對象處理這個請求
不保證每個請求都被接受,因為一個請求沒有一個明確的接收者的時候,不能保證一定會被處理,就會一直被傳遞下去
職責鏈模式包含如下角色:
- Handler: 抽象處理者
- ConcreteHandler: 具體處理者
- Client: 客戶類
在以下情況下可以使用職責鏈模式:
- 有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。
- 在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
- 可動態指定一組對象處理請求。
二. 命令模式(對象行為性模式)
定義:將一個請求封裝為一個對象,從而可用不同的請求對客戶進行參數化,將請求排隊或記錄請求日志,支持可撤銷的操作
是一種對象行為性模式,類似於傳統設計方法的回調機制,將一個請求封裝為一個對象,可用不同的請求對客戶進行參數化,將請求排隊或記錄請求日志,這樣做的目的就可以支持可撤銷的操作。對這個命令的封裝,將發出命令的責任和執行命令的責任分開,委派給不同的對象,這樣就能實現發送者和接收者的完全的解耦,以達到松耦合的目的,提高系統的可擴展性和靈活性
命令模式包含如下角色:
- Command: 抽象命令類
- ConcreteCommand: 具體命令類
- Invoker: 調用者
- Receiver: 接收者
- Client:客戶類
典型的調用者代碼:
public class Invoker { private Command command; public Invoker(Command command) { this.command=command; } public void setCommand(Command command) { this.command=command; }
//業務方法,用於調用命令類的方法
public void call() { command.execute(); } }
典型的具體命令類代碼:
public class ConcreteCommand extends Command { private Receiver receiver; public void execute() { receiver.action(); } }
典型的請求接收者代碼:
public class Receiver { public void action() { //具體操作 } }
三.解釋器模式(類行為性模式)
定義:給定一種語言,定義它的文法表示,並定義一個解釋器,該解釋器用來根據文法表示來解釋語言中的句子
四. 迭代器模式(對象行為性模式)
定義:提供一種方法來順序訪問一個聚合對象中的各個元素,而不需要暴露該對象的內部表示。
迭代器模式支持以不同的方式遍歷一個聚合對象,復雜的聚合可以采用多種方法遍歷,允許在同一個聚合上有多個遍歷,每個迭代器保持自己的遍歷狀態,這樣可以支持同時進行多個遍歷操作
迭代器模式包含如下角色:
Iterator: 抽象迭代器
ConcreteIterator: 具體迭代器
Aggregate: 抽象聚合類
ConcreteAggregate: 具體聚合類
模式適用環境
在以下情況下可以使用迭代器模式:
訪問一個聚合對象的內容而無須暴露它的內部表示。
需要為聚合對象提供多種遍歷方式。
為遍歷不同的聚合結構提供一個統一的接口。
五.中介者模式(對象行為性模式)
定義:用一個中介對象來封裝一系列的對象交互。它使各對象不需要顯示地相互調用,從而達到低耦合,還可以獨立地改變對象間的交互
中介對象的存在,就保證了對象結構上的穩定,也就是說,系統結構不會因為新的對象引用而導致大量的修改。
中介者模式包含如下角色:
- Mediator: 抽象中介者
- ConcreteMediator: 具體中介者
- Colleague: 抽象同事類
- ConcreteColleague: 具體同事類
中介者模式可以使對象之間的關系數量急劇減少:
中介者承擔兩方面的職責:
中轉作用(結構性):通過中介者提供的中轉作用,各個同事對象就不再需要顯式引用其他同事,當需要和其他同事進行通信時,通過中介者即可。該中轉作用屬於中介者在結構上的支持。
協調作用(行為性):中介者可以更進一步的對同事之間的關系進行封裝,同事可以一致地和中介者進行交互,而不需要指明中介者需要具體怎么做,中介者根據封裝在自身內部的協調邏輯,對同事的請求進行進一步處理,將同事成員之間的關系行為進行分離和封裝。該協調作用屬於中介者在行為上的支持。
典型的抽象中介者類代碼:
public abstract class Mediator { protected ArrayList colleagues; public void register(Colleague colleague) { colleagues.add(colleague); } }
典型的具體中介者類代碼:
public abstract void operation(); }
public class ConcreteMediator extends Mediator { public void operation() { ...... ((Colleague)(colleagues.get(0))).method1(); ...... } }
典型的抽象同事類代碼:
public abstract class Colleague { protected Mediator mediator; public Colleague(Mediator mediator) { this.mediator=mediator; } public abstract void method1(); public abstract void method2(); }
典型的具體同事類代碼:
public class ConcreteColleague extends Colleague { public ConcreteColleague(Mediator mediator) { super(mediator); } public void method1() { ...... } public void method2() { mediator.operation1(); } }
在以下情況下可以使用中介者模式:
系統中對象之間存在復雜的引用關系,產生的相互依賴關系結構混亂且難以理解。
一個對象由於引用了其他很多對象並且直接和這些對象通信,導致難以復用該對象。
想通過一個中間類來封裝多個類中的行為,而又不想生成太多的子類。可以通過引入中介者類來實現,在中介者中定義對象交互的公共行為,如果需要改變行為則可以增加新的中介者類。
六. 備忘錄模式
定義:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態,從而可以在以后將該對象恢復到原先保存的狀態
這樣做,就可以在以后,將這個對象恢復到原來的保存狀態。
備忘錄模式包含如下角色:
- Originator: 原發器
- Memento: 備忘錄
- Caretaker: 負責人
模式分析
由於在備忘錄中存儲的是原發器的中間狀態,因此需要防止原發器以外的其他對象訪問備忘錄。
備忘錄對象通常封裝了原發器的部分或所有的狀態信息,而且這些狀態不能被其他對象訪問,也就是說不能在備忘錄對象之外保存原發器狀態,因為暴露其內部狀態將違反封裝的原則,可能有損系統的可靠性和可擴展性。
為了實現對備忘錄對象的封裝,需要對備忘錄的調用進行控制:
- 對於原發器而言,它可以調用備忘錄的所有信息,允許原發器訪問返回到先前狀態所需的所有數據;
- 對於負責人而言,只負責備忘錄的保存並將備忘錄傳遞給其他對象;
- 對於其他對象而言,只需要從負責人處取出備忘錄對象並將原發器對象的狀態恢復,而無須關心備忘錄的保存細節。
理想的情況是只允許生成該備忘錄的那個原發器訪問備忘錄的內部狀態。
典型的原發器類代碼如下所示:
public class Originator { private String state; public Originator(){} // 創建一個備忘錄對象 public Memento createMemento(){ return new Memento(this); } // 根據備忘錄對象恢復原發器狀態 public void restoreMemento(Memento m){ state = m.state; } public void setState(String state) { this.state=state; } public String getState() { return this.state; } }
典型的備忘錄類代碼如下所示:
class Memento { private String state; public Memento(Originator o){ state = o.state; } public void setState(String state) { this.state=state; } public String getState() { return this.state; } }
典型的負責人類代碼如下所示:
public class Caretaker { private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento=memento; } }
七. 觀察模式(對象行為性模式)
定義:定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並自動更新
又被稱為發布-訂閱模式,命令模式的優點,在於實現了表示層和數據層的分離,並且定義了穩定地更新消息的傳遞機制,類別很清晰,對接口也進行了抽象,使得相同的數據層可以有不同的表示層,mvc就屬於這種。
在以下情況下可以使用觀察者模式:
一個抽象模型有兩個方面,其中一個方面依賴於另一個方面。將這些方面封裝在獨立的對象中使它們可以各自獨立地改變和復用。
一個對象的改變將導致其他一個或多個對象也發生改變,而不知道具體有多少對象將發生改變,可以降低對象之間的耦合度。
一個對象必須通知其他對象,而並不知道這些對象是誰。
需要在系統中創建一個觸發鏈,A對象的行為將影響B對象,B對象的行為將影響C對象……,可以使用觀察者模式創建一種鏈式觸發機制。
MVC模式
MVC模式是一種架構模式,它包含三個角色:模型(Model),視圖(View)和控制器(Controller)。觀察者模式可以用來實現MVC模式,觀察者模式中的觀察目標就是MVC模式中的模型(Model),而觀察者就是MVC中的視圖(View),控制器(Controller)充當兩者之間的中介者(Mediator)。當模型層的數據發生改變時,視圖層將自動改變其顯示內容。
八.狀態模式(對象行為性模式)
在很多情況下,一個對象的行為取決於一個或多個動態變化的屬性,這樣的屬性叫做狀態,這樣的對象叫做有狀態的 (stateful)對象,這樣的對象狀態是從事先定義好的一系列值中取出的。當一個這樣的對象與外部事件產生互動時,其內部狀態就會改變,從而使得系統的行為也隨之發生變化
定義:允許一個對象在其內部狀態改變時能夠改變它的行為
從表面上看,對象好像修改了它的類,狀態模式封裝了狀態的轉換過程,但是它需要事先枚舉狀態的種類,這樣事先確定狀態的種類,導致在狀態模式中新加狀態時,可能會違反開閉原則。對新的狀態的引用,需要修改與它能夠轉換的其它狀態類的代碼,而且狀態類的使用,會增加類和對象的個數。
狀態模式包含如下角色:
- Context: 環境類
- State: 抽象狀態類
- ConcreteState: 具體狀態類
模式分析
狀態模式描述了對象狀態的變化以及對象如何在每一種狀態下表現出不同的行為。
狀態模式的關鍵是引入了一個抽象類來專門表示對象的狀態,這個類我們叫做抽象狀態類,而對象的每一種具體狀態類都繼承了該類,並在不同具體狀態類中實現了不同狀態的行為,包括各種狀態之間的轉換。
在狀態模式結構中需要理解環境類與抽象狀態類的作用:
環境類實際上就是擁有狀態的對象,環境類有時候可以充當狀態管理器(State Manager)的角色,可以在環境類中對狀態進行切換操作。
抽象狀態類可以是抽象類,也可以是接口,不同狀態類就是繼承這個父類的不同子類,狀態類的產生是由於環境類存在多個狀態,同時還滿足兩個條件:這些狀態經常需要切換,在不同的狀態下對象的行為不同。因此可以將不同對象下的行為單獨提取出來封裝在具體的狀態類中,使得環境類對象在其內部狀態改變時可以改變它的行為,對象看起來似乎修改了它的類,而實際上是由於切換到不同的具體狀態類實現的。由於環境類可以設置為任一具體狀態類,因此它針對抽象狀態類進行編程,在程序運行時可以將任一具體狀態類的對象設置到環境類中,從而使得環境類可以改變內部狀態,並且改變行為。
狀態模式的優點
封裝了轉換規則。
枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類。
將所有與某個狀態有關的行為放到一個類中,並且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行為。
允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。
可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數。
狀態模式的缺點
狀態模式的使用必然會增加系統類和對象的個數。
狀態模式的結構與實現都較為復雜,如果使用不當將導致程序結構和代碼的混亂。
狀態模式對“開閉原則”的支持並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態;而且修改某個狀態類的行為也需修改對應類的源代碼。
在以下情況下可以使用狀態模式:
對象的行為依賴於它的狀態(屬性)並且可以根據它的狀態改變而改變它的相關行為。
代碼中包含大量與對象狀態有關的條件語句,這些條件語句的出現,會導致代碼的可維護性和靈活性變差,不能方便地增加和刪除狀態,使客戶類與類庫之間的耦合增強。在這些條件語句中包含了對象的行為,而且這些條件對應於對象的各種狀態。
九. 策略模式(對象行為性模式)
定義:定義一系列算法,把它們一個個封裝起來,並且使它們之間可相互替換,從而讓算法可以獨立於使用它的用戶而變化
目的是使行為和環境相分隔,當出現新的行為時,只需要實現新的策略類。
策略模式包含如下角色:
- Context: 環境類
- Strategy: 抽象策略類
- ConcreteStrategy: 具體策略類
抽象策略類
public abstract class AbstractStrategy
{
public abstract void algorithm();
}
具體策略類
public class ConcreteStrategyA extends AbstractStrategy
{
public void algorithm()
{
//算法A
}
}
環境類
public class Context
{
private AbstractStrategy strategy;
public void setStrategy(AbstractStrategy strategy)
{
this.strategy= strategy;
}
public void algorithm()
{
strategy.algorithm();
}
}
客戶端代碼
Context context = new Context();
AbstractStrategy strategy;
strategy = new ConcreteStrategyA();
context.setStrategy(strategy);
context.algorithm();
策略模式的缺點
客戶端必須知道所有的策略類,並自行決定使用哪一個策略類。
策略模式將造成產生很多策略類,可以通過使用享元模式在一定程度上減少對象的數量。
在以下情況下可以使用策略模式:
如果在一個系統里面有許多類,它們之間的區別僅在於它們的行為,那么使用策略模式可以動態地讓一個對象在許多行為中選擇一種行為。
一個系統需要動態地在幾種算法中選擇一種。
如果一個對象有很多的行為,如果不用恰當的模式,這些行為就只好使用多重的條件選擇語句來實現。
不希望客戶端知道復雜的、與算法相關的數據結構,在具體策略類中封裝算法和相關的數據結構,提高算法的保密性與安全性。
策略模式與狀態模式
可以通過環境類狀態的個數來決定是使用策略模式還是狀態模式。
策略模式的環境類自己選擇一個具體策略類,具體策略類無須關心環境類;而狀態模式的環境類由於外在因素需要放進一個具體狀態中,以便通過其方法實現狀態的切換,因此環境類和狀態類之間存在一種雙向的關聯關系。
使用策略模式時,客戶端需要知道所選的具體策略是哪一個,而使用狀態模式時,客戶端無須關心具體狀態,環境類的狀態會根據用戶的操作自動轉換。
如果系統中某個類的對象存在多種狀態,不同狀態下行為有差異,而且這些狀態之間可以發生轉換時使用狀態模式;如果系統中某個類的某一行為存在多種實現方式,而且這些實現方式可以互換時使用策略模式。
十. 模板模式(類行為性模式)
定義:定義一個操作中的算法骨架,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結構即可重新定義算法中某些特定步驟
缺點是對不同的實現都需要定義一個子類,導致類的個數會增加,但是更符合類的職責分配的原則,使類的內聚性提高
十一.訪問者模式(對象行為性模式)
定義:表示一個作用於某對象結構中的各元素的操作,使得在不改變各元素的類的前提下定義作用於這些元素的新操作
使得增加新的操作很容易,但是犧牲了類的封裝性。
模式動機
在實際使用時,對同一集合對象的操作並不是唯一的,對相同的元素對象可能存在多種不同的操作方式。
而且這些操作方式並不穩定,可能還需要增加新的操作,以滿足新的業務需求。
此時,訪問者模式就是一個值得考慮的解決方案。
訪問者模式的目的是封裝一些施加於某種數據結構元素之上的操作,一旦這些操作需要修改的話,接受這個操作的數據結構可以保持不變。為不同類型的元素提供多種訪問操作方式,且可以在不修改原有系統的情況下增加新的操作方式,這就是訪問者模式的模式動機。
訪問者模式包含如下角色:
Vistor: 抽象訪問者
ConcreteVisitor: 具體訪問者
Element: 抽象元素
ConcreteElement: 具體元素
ObjectStructure: 對象結構
典型的抽象訪問者類代碼如下所示:
public abstract class Visitor
{
public abstract void visit(ConcreteElementA elementA);
public abstract void visit(ConcreteElementB elementB);
public void visit(ConcreteElementC elementC)
{
//元素ConcreteElementC操作代碼
}
}
典型的具體訪問者類代碼如下所示:
public class ConcreteVisitor extends Visitor
{
public void visit(ConcreteElementA elementA)
{
//元素ConcreteElementA操作代碼
}
public void visit(ConcreteElementB elementB)
{
//元素ConcreteElementB操作代碼
}
}
典型的抽象元素類代碼如下所示:
public interface Element
{
public void accept(Visitor visitor);
}
典型的具體元素類代碼如下所示:
public class ConcreteElementA implements Element
{
public void accept(Visitor visitor)
{
visitor.visit(this);
}
public void operationA()
{
//業務方法
}
}
典型的對象結構類代碼如下所示:
public class ObjectStructure
{
private ArrayList list=new ArrayList();
public void accept(Visitor visitor)
{
Iterator i=list.iterator();
while(i.hasNext())
{
((Element)i.next()).accept(visitor);
}
}
public void addElement(Element element)
{
list.add(element);
}
public void removeElement(Element element)
{
list.remove(element);
}
}
在以下情況下可以使用訪問者模式:
一個對象結構包含很多類型的對象,希望對這些對象實施一些依賴其具體類型的操作。在訪問者中針對每一種具體的類型都提供了一個訪問操作,不同類型的對象可以有不同的訪問操作。
需要對一個對象結構中的對象進行很多不同的並且不相關的操作,而需要避免讓這些操作“污染”這些對象的類,也不希望在增加新操作時修改這些類。訪問者模式使得我們可以將相關的訪問操作集中起來定義在訪問者類中,對象結構可以被多個不同的訪問者類所使用,將對象本身與對象的訪問操作分離。
對象結構中對象對應的類很少改變,但經常需要在此對象結構上定義新的操作。
狀態圖實例: