策略模式:

1、定義:定義了一系列演算法,并將每個演算法封裝起來,使它們可以相互替換,且演算法的變化不會影響使用演算法的客戶
2、模型結構:
(1)抽象策略(Strategy)類:定義了一個公共介面,各種不同的演算法以不同的方式實作這個介面,
環境角色使用這個介面呼叫不同的演算法,一般使用介面或抽象類實作
(2)具體策略(Concrete Strategy)類:實作了抽象策略定義的介面,提供具體的演算法實作
(3)環境(Context)類:持有一個策略類的參考,最終給客戶端呼叫
3、優點:
(1)使用策略模式可以避免使用多重條件轉移陳述句
(2)提供一系列可供重用的演算法族,恰當使用繼承可以把演算法族的公共代碼轉移到父類里面,從而避免重復的代碼
(3)提供了對“開閉原則”的完美支持,在不修改原有系統的基礎上選擇演算法或行為,也可靈活地增加新演算法或行為
(4)策略模式可以提供相同行為的不同實作,客戶可根據不同的要求選擇不同的實作
(5)策略模式把演算法的使用放到環境類中,而演算法的實作移到具體策略類中,實作了二者的分離
4、缺點:
(1)客戶端必須理解所有策略演算法的區別,以便適時選擇恰當的演算法類
(2)策略模式將造成產生很多策略類,可以通過使用享元模式在一定程度上減少物件的數量
5、適用環境:
(1)一個系統需要動態地在幾種演算法中選擇一種
(2)一個類定義了多種行為,并且這些行為在這個類的操作中以多個條件陳述句的形式出現,
可將每個條件分支移入它們各自的策略類中以代替這些條件陳述句
(3)不希望客戶端知道復雜的、與演算法相關的資料結構,在具體策略類中封裝演算法和相關的資料結構,
提高演算法的保密性與安全性
(4)多個類只區別在表現行為不同,可以使用策略模式,在運行時動態選擇具體要執行的行為
(5)系統中各演算法彼此完全獨立,且要求對客戶隱藏具體演算法的實作細節時
// 購買門票策略(門票每張10元): 1、超過25張的打九折 2、15張票送一張// 抽象策略類abstract class Strategy { protected price: number = 10; // 門票價格 protected total: number; // 門票總花費 // 根據方案不同計算總花費 abstract strategyMethod(num: number): void;}// 具體策略類,超過25張的打九折class ConcreteStrategyA extends Strategy { strategyMethod(num: number): void { this.total = num > 25 ? 25*this.price+(num-25)*this.price*0.9 : num*this.price; console.log(`方法 1 購買 ${num} 張票花費 ${this.total} 元`); }}// 15張票送一張class ConcreteStrategyB extends Strategy { strategyMethod(num: number): void { this.total = 15*this.price*((num-num%16)/16) + (num%16)*this.price; console.log(`方法 2 購買 ${num} 張票花費 ${this.total} 元`); }}// 環境類class StrategyContext { private strategy: Strategy; getStrategy(): Strategy { return this.strategy; } setStrategy(strategy: Strategy) { this.strategy = strategy; } strategyMethod(num: number): void { this.strategy.strategyMethod(num); }}let fares: number = 100;let strategyContext: StrategyContext = new StrategyContext();let strategy1: Strategy = new ConcreteStrategyA();strategyContext.setStrategy(strategy1); strategyContext.strategyMethod(fares); // 方法 1 購買 100 張票花費 925 元let strategy2: Strategy = new ConcreteStrategyB();strategyContext.setStrategy(strategy2); strategyContext.strategyMethod(fares); // 方法 2 購買 100 張票花費 940 元// 目前環境里的策略為 strategy2,所以輸出:方法 2 購買 100 張票花費 940 元strategyContext.getStrategy().strategyMethod(fares);
模板方法模式:

1、定義:定義一個操作中的演算法骨架,而將演算法的一些步驟延遲到子類中,
使得子類可以不改變該演算法結構的情況下重定義該演算法的某些特定步驟
2、模型結構:
(1)抽象類(Abstract Class):負責給出一個演算法的輪廓和骨架,它由一個模板方法和若干個基本方法構成
(2)具體子類(Concrete Class):實作抽象類中所定義的抽象方法和鉤子方法,它們是一個頂級邏輯的一個組成步驟
注:
(1)模板方法:定義了演算法的骨架,按某種順序呼叫其包含的基本方法
(2)基本方法:是整個演算法中的一個步驟,包含以下幾種型別
A、抽象方法:在抽象類中申明,由具體子類實作
B、具體方法:在抽象類中已經實作,在具體子類中可以繼承或重寫它
C、鉤子方法:在抽象類中已經實作,包括用于判斷的邏輯方法和需要子類重寫的空方法兩種
3、優點:
(1)封裝了不變部分,擴展可變部分:它把認為是不變部分的演算法封裝到父類中實作,
而把可變部分演算法由子類繼承實作,便于子類繼續擴展
(2)在父類中提取公共部分的代碼,便于代碼復用
(3)部分方法是由子類實作的,因此子類可以通過擴展方式增加相應的功能,符合“開閉原則”
4、缺點:
(1)對每個不同的實作都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象
(2)父類中的抽象方法由子類實作,子類執行的結果會影響父類的結果,
這導致一種反向的控制結構,它提高了代碼閱讀的難度
5、適用環境:
(1)演算法的整體步驟很固定,但其中個別部分易變時,這時候可以使用模板方法模式,
將容易變的部分抽象出來,供子類實作
(2)當多個子類存在公共的行為時,可以將其提取出來并集中到一個公共父類中以避免代碼重復,
首先,要識別現有代碼中的不同之處,并且將不同之處分離為新的操作,
最后,用一個呼叫這些新的操作的模板方法來替換這些不同的代碼
(3)當需要控制子類的擴展時,模板方法只在特定點呼叫鉤子操作,這樣就只允許在這些點進行擴展
// 假設早上上班步驟:1、起床 2、刷牙洗臉 3、吃早飯 4、坐車去公司 5、到達時間 // 抽象類abstract class AbstractClass { protected food: string; protected vehicle: string; protected time: string; TemplateMethod(): void { this.awake(); this.clean(); this.eat(); this.transportation(); this.arrive(); } awake(): void { console.log("Awaking on time..."); } clean(): void { console.log("Washing face and brushing teeth..."); } abstract eat():void; abstract transportation(): void; abstract arrive(): void;}// 具體子類class ConcreteClass extends AbstractClass { constructor(food: string, vehicle: string, time: string) { super(); this.food = food; this.vehicle = vehicle; this.time = time; } eat(): void { console.log(`Eating ${this.food}`); } transportation(): void { console.log(`Going to workplace by ${this.vehicle}`); } arrive(): void { console.log(`Arriving workplace at ${this.time}`); }}let food: string = "bread and milk";let vehicle: string = "bus";let time: string = "9:00 am";let tm: AbstractClass = new ConcreteClass(food, vehicle, time);tm.TemplateMethod();
訪問者模式:

1、定義:將作用于某種資料結構中的各元素的操作分離出來封裝成獨立的類,
使其在不改變資料結構的前提下可以添加作用于這些元素的新的操作,
為資料結構中的每個元素提供多種訪問方式
2、模型結構:
(1)抽象訪問者(Visitor):定義一個訪問具體元素的介面,為每個具體元素類對應一個訪問操作 visit(),
該操作中的引數型別標識了被訪問的具體元素
(2)具體訪問者(ConcreteVisitor):實作抽象訪問者中宣告的各個訪問操作,確定訪問者訪問一個元素時該做什么
(3)抽象元素(Element):宣告一個包含接受操作 accept() 的介面,被接受的訪問者物件作為 accept() 方法的引數
(4)具體元素(ConcreteElement):實作抽象元素角色提供的 accept() 操作,其方法體通常是 visitor.visit(this),
另外具體元素中可能還包含本身業務邏輯的相關操作
(5)物件結構(Object Structure):是一個包含元素角色的容器,提供讓訪問者物件遍歷容器中的所有元素的方法
3、優點:
(1)擴展性好:能夠在不修改物件結構中的元素的情況下,為物件結構中的元素添加新的功能
(2)復用性好:可以通過訪問者來定義整個物件結構通用的功能,從而提高系統的復用程度
(3)靈活性好:將資料結構與作用于結構上的操作解耦,使操作集合可相對自由地演化而不影響系統的資料結構
(4)符合單一職責原則:把相關的行為封裝在一起,構成一個訪問者,使每一個訪問者的功能都比較單一
4、缺點:
(1)增加新的元素類很困難:在訪問者模式中,每增加一個新的元素類,
都要在每一個具體訪問者類中增加相應的具體操作,這違背了“開閉原則”
(2)破壞封裝:訪問者模式中具體元素對訪問者公布細節,這破壞了物件的封裝性
(3)違反了依賴倒置原則:訪問者模式依賴了具體類,而沒有依賴抽象類
5、適用環境:
(1)物件結構相對穩定,但其操作演算法經常變化的程式
(2)物件結構中的物件需要提供多種不同且不相關的操作,而且要避免讓這些操作的變化影響物件的結構
(3)物件結構包含很多型別的物件,希望對這些物件實施一些依賴于其具體型別的操作
// 抽象訪問者interface Visitor { visit(element: IElement): void; }// 具體訪問者,籃球運動員和羽毛球運動員class ConcreteVisitorA implements Visitor { visit(element: IElement): void { console.log(`籃球運動員:${element.operationA()}`); }}class ConcreteVisitorB implements Visitor { visit(element: IElement): void { console.log(`羽毛球運動員:${element.operationB()}`); }}// 抽象元素interface IElement { accept(visitor: Visitor): void; operationA(): string; operationB(): string;}// 具體元素,球類和工具類class ConcreteElementA implements IElement { accept(visitor: Visitor): void { visitor.visit(this); } operationA(): string { return "打籃球"; } operationB(): string { return "打羽毛球"; }}class ConcreteElementB { accept(visitor: Visitor): void { visitor.visit(this); } operationA(): string { return "需要籃球鞋"; } operationB(): string { return "需要羽毛球拍"; }}// 物件結構class ObjectStructure { private elems: IElement[] = new Array<IElement>(); accept(visitor: Visitor): void { for (let elem of this.elems) { elem.accept(visitor); } } add(elem: IElement): void { this.elems.push(elem); } remove(elem: IElement): void { let idx: number = this.elems.indexOf(elem); this.elems.splice(idx, 1); }}let eleA: IElement = new ConcreteElementA();let eleB: IElement = new ConcreteElementB();let obs: ObjectStructure = new ObjectStructure();obs.add(eleA);obs.add(eleB);let visitor: Visitor = new ConcreteVisitorA();obs.accept(visitor);visitor = new ConcreteVisitorB();obs.remove(eleA);obs.accept(visitor);
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/39916.html
標籤:設計模式
上一篇:設計模式-行為型-迭代器模式
