策略模式:算法
一、定義:定義了一系列算法,並將每一個算法封裝起來,使它們能夠相互替換,且算法的變化不會影響使用算法的客戶安全
二、模型結構:數據結構
(1)抽象策略(Strategy)類:定義了一個公共接口,各類不一樣的算法以不一樣的方式實現這個接口,工具
環境角色使用這個接口調用不一樣的算法,通常使用接口或抽象類實現this
(2)具體策略(Concrete Strategy)類:實現了抽象策略定義的接口,提供具體的算法實現spa
(3)環境(Context)類:持有一個策略類的引用,最終給客戶端調用設計
三、優勢:code
(1)使用策略模式能夠避免使用多重條件轉移語句對象
(2)提供一系列可供重用的算法族,恰當使用繼承能夠把算法族的公共代碼轉移到父類裏面,從而避免重複的代碼blog
(3)提供了對「開閉原則」的完美支持,在不修改原有系統的基礎上選擇算法或行爲,也可靈活地增長新算法或行爲
(4)策略模式能夠提供相同行爲的不一樣實現,客戶可根據不一樣的要求選擇不一樣的實現
(5)策略模式把算法的使用放到環境類中,而算法的實現移到具體策略類中,實現了兩者的分離
四、缺點:
(1)客戶端必須理解全部策略算法的區別,以便適時選擇恰當的算法類
(2)策略模式將形成產生不少策略類,能夠經過使用享元模式在必定程度上減小對象的數量
五、適用環境:
(1)一個系統須要動態地在幾種算法中選擇一種
(2)一個類定義了多種行爲,而且這些行爲在這個類的操做中以多個條件語句的形式出現,
可將每一個條件分支移入它們各自的策略類中以代替這些條件語句
(3)不但願客戶端知道複雜的、與算法相關的數據結構,在具體策略類中封裝算法和相關的數據結構,
提升算法的保密性與安全性
(4)多個類只區別在表現行爲不一樣,可使用策略模式,在運行時動態選擇具體要執行的行爲
(5)系統中各算法彼此徹底獨立,且要求對客戶隱藏具體算法的實現細節時
// 購買門票策略(門票每張10元): 一、超過25張的打九折 二、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)抽象類(Abstract Class):負責給出一個算法的輪廓和骨架,它由一個模板方法和若干個基本方法構成
(2)具體子類(Concrete Class):實現抽象類中所定義的抽象方法和鉤子方法,它們是一個頂級邏輯的一個組成步驟
注:
(1)模板方法:定義了算法的骨架,按某種順序調用其包含的基本方法
(2)基本方法:是整個算法中的一個步驟,包含如下幾種類型
A、抽象方法:在抽象類中申明,由具體子類實現
B、具體方法:在抽象類中已經實現,在具體子類中能夠繼承或重寫它
C、鉤子方法:在抽象類中已經實現,包括用於判斷的邏輯方法和須要子類重寫的空方法兩種
三、優勢:
(1)封裝了不變部分,擴展可變部分:它把認爲是不變部分的算法封裝到父類中實現,
而把可變部分算法由子類繼承實現,便於子類繼續擴展
(2)在父類中提取公共部分的代碼,便於代碼複用
(3)部分方法是由子類實現的,所以子類能夠經過擴展方式增長相應的功能,符合「開閉原則」
四、缺點:
(1)對每一個不一樣的實現都須要定義一個子類,這會致使類的個數增長,系統更加龐大,設計也更加抽象
(2)父類中的抽象方法由子類實現,子類執行的結果會影響父類的結果,
這致使一種反向的控制結構,它提升了代碼閱讀的難度
五、適用環境:
(1)算法的總體步驟很固定,但其中個別部分易變時,這時候可使用模板方法模式,
將容易變的部分抽象出來,供子類實現
(2)當多個子類存在公共的行爲時,能夠將其提取出來並集中到一個公共父類中以免代碼重複。
首先,要識別現有代碼中的不一樣之處,而且將不一樣之處分離爲新的操做。
最後,用一個調用這些新的操做的模板方法來替換這些不一樣的代碼
(3)當須要控制子類的擴展時,模板方法只在特定點調用鉤子操做,這樣就只容許在這些點進行擴展
// 假設早上上班步驟:一、起牀 二、刷牙洗臉 三、吃早飯 四、坐車去公司 五、到達時間 // 抽象類 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)抽象訪問者(Visitor):定義一個訪問具體元素的接口,爲每一個具體元素類對應一個訪問操做 visit(),
該操做中的參數類型標識了被訪問的具體元素
(2)具體訪問者(ConcreteVisitor):實現抽象訪問者中聲明的各個訪問操做,肯定訪問者訪問一個元素時該作什麼
(3)抽象元素(Element):聲明一個包含接受操做 accept() 的接口,被接受的訪問者對象做爲 accept() 方法的參數
(4)具體元素(ConcreteElement):實現抽象元素角色提供的 accept() 操做,其方法體一般是 visitor.visit(this),
另外具體元素中可能還包含自己業務邏輯的相關操做
(5)對象結構(Object Structure):是一個包含元素角色的容器,提供讓訪問者對象遍歷容器中的全部元素的方法
三、優勢:
(1)擴展性好:可以在不修改對象結構中的元素的狀況下,爲對象結構中的元素添加新的功能
(2)複用性好:能夠經過訪問者來定義整個對象結構通用的功能,從而提升系統的複用程度
(3)靈活性好:將數據結構與做用於結構上的操做解耦,使操做集合可相對自由地演化而不影響系統的數據結構
(4)符合單一職責原則:把相關的行爲封裝在一塊兒,構成一個訪問者,使每個訪問者的功能都比較單一
四、缺點:
(1)增長新的元素類很困難:在訪問者模式中,每增長一個新的元素類,
都要在每個具體訪問者類中增長相應的具體操做,這違背了「開閉原則」
(2)破壞封裝:訪問者模式中具體元素對訪問者公佈細節,這破壞了對象的封裝性
(3)違反了依賴倒置原則:訪問者模式依賴了具體類,而沒有依賴抽象類
五、適用環境:
(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);