行為型設計模式(下)

来源:https://www.cnblogs.com/lemonyam/archive/2019/10/08/11626281.html
-Advertisement-
Play Games

策略模式: 1、定義:定義了一系列演算法,並將每個演算法封裝起來,使它們可以相互替換,且演算法的變化不會影響使用演算法的客戶 2、模型結構: (1)抽象策略(Strategy)類:定義了一個公共介面,各種不同的演算法以不同的方式實現這個介面, 環境角色使用這個介面調用不同的演算法,一般使用介面或抽象類實現 (2 ...


策略模式:

  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);

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 解構:ES6允許按照一定模式,從數組和對象中提取值,對變數進行賦值。 對象的解構與數組有一個重要的不同。數組的元素是按次序排列的,變數的取值由他的位置決定;而對象的屬性沒有次序,變數必須與屬性同名,才能取到正確的值。 變數解構賦值的用途:交換變數的值,從函數返回多個值,函數參數的定義,提取JSON數 ...
  • Let:用來聲明變數,但是所聲明的變數,只在let命令所在的代碼塊內生效。 變數提升:變數可以在聲明之前使用,值為undefined。 暫時性死區:在代碼塊內,使用let命令聲明變數之前,該變數都是不可用的。 不允許重覆聲明:let不允許在相同作用域內,重覆聲明同一變數。 Const:const聲明 ...
  • 本文僅限於入門級,沒有成規模製作,希望能對你有所幫助。 因為在開發多個項目中可能會用到同一個組件,那麼我們通過複製粘貼的形式更新,無異於是笨拙的,我們可以通過上傳到npm後,不斷迭代npm包來實現更新。 前期準備 初始化project 這裡我們使用 來初始化一個vue項目。 or 首先我們來開發一個 ...
  • 垃圾分類,一般是指按一定規定或標准將垃圾分類儲存、分類投放和分類搬運,從而轉變成公共資源的一系列活動的總稱。分類的目的是提高垃圾的資源價值和經濟價值,力爭物盡其用。垃圾在分類儲存階段屬於公眾的私有品,垃圾經公眾分類投放後成為公眾所在小區或社區的區域性準公共資源,垃圾分類搬運到垃圾集中點或轉運站後成... ...
  • 一.開通QQ服務 "點我進入QQ推廣官網" 然後點擊 後面自己看中文 二.頁面a標簽 三.js實現 ...
  • 什麼是微服務?為什麼會有微服務?讓我們帶著這些疑問開始我們的探索。 我們先看下維基百科和百度百科給出的定義: 維基百科:2014年,Martin Fowler 與 James Lewis 共同提出了微服務的概念,定義了微服務是由以單一應用程式構成的小服務,自己擁有自己的行程與輕量化處理,服務依業務功 ...
  • 0--前言 spring cloud的服務註冊中心,該選擇誰?在選擇前,我們首先需要來瞭解下分散式的CAP定理: 所謂CAP,是指: Consistency:一致性;就是在分散式系統中的所有數據備份,在同一時刻是否同樣的值 Availability:可用性;就是負載過大後,集群整體是否還能響應客戶端 ...
  • 淘寶架構 我們以淘寶架構為例,瞭解下大型的電商項目的服務端的架構是怎樣,如圖所示 上面是一些安全體繫系統,如數據安全體系、應用安全體系、前端安全體系等。 中間是業務運營服務系統,如會員服務、商品服務、店鋪服務、交易服務等。 還有共用業務,如分散式數據層、數據分析服務、配置服務、數據搜索服務等。 最下 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...