行為型設計模式(中)

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

中介者模式: 1、定義:用一個中介對象來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用, 從而使其耦合鬆散,而且可以獨立地改變它們之間的交互 2、模型結構: (1)抽象中介者(Mediator):它是中介者的介面,提供了同事對象註冊與轉發同事對象信息的抽象方法 (2)具體中介者(Concr ...


中介者模式:

  1、定義:用一個中介對象來封裝一系列的對象交互,中介者使各對象不需要顯式地相互引用,

        從而使其耦合鬆散,而且可以獨立地改變它們之間的交互

  2、模型結構:

    (1)抽象中介者(Mediator):它是中介者的介面,提供了同事對象註冊與轉發同事對象信息的抽象方法

    (2)具體中介者(ConcreteMediator):實現中介者介面,通過一個數據結構來管理同事對象

                          協調各個同事角色之間的交互關係,因此它依賴於同事角色

    (3)抽象同事類(Colleague):定義同事類的介面,保存中介者對象,提供同事對象交互的抽象方法,

                     實現所有相互影響的同事類的公共功能

    (4)具體同事類(Concrete Colleague):抽象同事類的實現者,當與其他同事對象交互時,中介者對象負責後續的交互

  3、優點:

    (1)降低了對象之間的耦合性,使得對象易於獨立地被覆用

    (2)將對象間的一對多關聯轉變為一對一的關聯,提高系統的靈活性,使得系統易於維護和擴展

    (3)減少子類生成

  4、缺點:當同事類太多時,中介者的職責將很大,它會變得複雜而龐大,以至於系統難以維護

  5、適用環境:

    (1)當對象之間存在複雜的網狀結構關係而導致依賴關係混亂難以復用

    (2)當想創建一個運行於多個類之間的對象,又不想生成新的子類時

// 抽象中介者
abstract class Mediator {
    abstract register(colleague: Colleague): void;
    abstract relay(colleague: Colleague): void;
}
// 具體中介者
class ConcreteMediator extends Mediator {
    // 儲存同事類集合
    private colleagues: Set<Colleague> = new Set<Colleague>();
    // 添加未加入的同事並設置中介
    register(colleague: Colleague): void {
        if (!this.colleagues.has(colleague)) {
            this.colleagues.add(colleague);
            colleague.setMedium(this);  
        }
    }
    // 中介接受請求
    relay(colleague: Colleague): void {
        if (this.colleagues.has(colleague)) {
            colleague.receive();
        }
    }
}
// 抽象同事類
abstract class Colleague {
    protected mediator: Mediator;   // 中介對象
    protected colleague: Colleague; // 要查詢的同事對象
    protected phone: string;    // 查詢內容
    constructor(phone: string) {
        this.phone = phone;
    }
    // 設置中介
    setMedium(mediator: Mediator): void {
        this.mediator = mediator;
    }
    abstract searchColleague(colleague: Colleague): void;   // 查詢同事
    abstract receive(): void;   // 接受請求
    abstract send(): void;  // 發送請求
}
// 具體同事類
class Tom extends Colleague {
    constructor(phone: string) {
        super(phone);
    }
    searchColleague(colleague: Colleague): void {
        this.colleague = colleague;
    }
    receive(): void {
        console.log(`Tom 收到請求,,電話號碼為${this.phone}`);
    }
    send(): void {
        console.log("Tom 發出電話查詢請求");
        this.mediator.relay(this.colleague);
    }
}
class Jerry extends Colleague {
    constructor(phone: string) {
        super(phone);
    }
    searchColleague(colleague: Colleague): void {
        this.colleague = colleague;
    }
    receive(): void {
        console.log(`Jerry 收到請求,電話號碼為${this.phone}`);
    }
    send(): void {
        console.log("Jerry 發出電話查詢請求");
        this.mediator.relay(this.colleague);
    }
}

let mediator: Mediator = new ConcreteMediator();
let colleague1: Colleague = new Tom("1234567");
let colleague2: Colleague = new Jerry("2234567");
// 設置相同的中介
mediator.register(colleague1);
mediator.register(colleague2);
colleague1.searchColleague(colleague2); // Tom 查詢 Jerry
colleague2.searchColleague(colleague1); // Jerry 查詢 Tom
colleague1.send();
console.log("--------------------------");
colleague2.send();

 

備忘錄模式:

  1、定義:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,併在該對象之外保存這個狀態,

        以便以後當需要時能將該對象恢復到原先保存的狀態

  2、模型結構:

    (1)發起人(Originator):記錄當前時刻的內部狀態信息,提供創建備忘錄和恢復備忘錄數據的功能

                   實現其他業務功能,它可以訪問備忘錄里的所有信息

    (2)備忘錄(Memento):負責存儲發起人的內部狀態,在需要的時候提供這些內部狀態給發起人

    (3)管理者(Caretaker):管理備忘錄,提供保存與獲取備忘錄的功能,但其不能對備忘錄的內容進行訪問與修改

  3、優點:

    (1)提供了一種可以恢復狀態的機制:當用戶需要時能夠比較方便地將數據恢復到某個歷史的狀態

    (2)實現了內部狀態的封裝:除了創建它的發起人之外,其他對象都不能夠訪問這些狀態信息

    (3)簡化了發起人類:發起人不需要管理和保存其內部狀態的各個備份,所有狀態信息都保存在備忘錄中,

                並由管理者進行管理,這符合單一職責原則

  4、缺點:資源消耗大,如果要保存的內部狀態信息過多或者特別頻繁,將會占用比較大的記憶體資源

  5、適用環境:

    (1)需要保存與恢複數據的場景

    (2)需要提供一個可回滾操作的場景

// 學生成績
class Grade {
    private no: string; // 學號
    private grade: number;  // 成績
    constructor(no: string, grade: number) {
        this.no = no;
        this.grade = grade;
    }
    // 輸出學號和成績
    printGrade(): void {
        console.log(`學號:${this.no},成績:${this.grade}`);
    }
}

// 備忘錄
class Memento {
    private grades: Grade[];
    constructor(grades: Grade[]) {
        this.grades = grades;
    }
    // 設置成績狀態
    setState(grades: Grade[]): void {
        this.grades = grades;
    }
    // 獲取成績狀態
    getState(): Grade[] {
        return this.grades;
    }
}
// 發起人
class Originator {
    private grades: Grade[];
    setState(grades: Grade[]): void {
        this.grades = grades;
    }
    getState(): Grade[] {
        return this.grades;
    }
    // 輸出成績狀態
    printState(): void {
        for (let grade of this.grades) {
            grade.printGrade();
        }
    }
    createMemento(): Memento {
        return new Memento(this.grades);
    }
    restoreMemento(mem: Memento): void {
        this.setState(mem.getState());
    }
}
// 管理者
class Caretaker {
    private memento: Memento = null;
    setMemento(mem: Memento): void {
        this.memento = mem;
    }
    getMemento(): Memento {
        return this.memento;
    }
}

// 初始化學生成績
let grades: Grade[] = new Array<Grade>();
let temp: Grade[] = new Array<Grade>();

for (let i: number = 0; i < 3; ++i) {
    grades.push(new Grade(String(i+1), 2019+i));
    temp.push(new Grade(String(i+2), i+1));
}

let ori: Originator = new Originator();
let cr: Caretaker = new Caretaker();

ori.setState(grades);
console.log("初始狀態:")
ori.printState();

cr.setMemento(ori.createMemento()); // 保存狀態
// 不能只修改 grades 數組再然後把該數組設置為新狀態,因為數組是引用類型
ori.setState(temp);
console.log("新的狀態:");
ori.printState();

ori.restoreMemento(cr.getMemento());    // 恢復狀態
console.log("恢復狀態:");
ori.printState();

 

觀察者模式:

  1、定義:定義對象間的一種一對多依賴關係,使得每當一個對象狀態發生改變時,

        其相關依賴對象皆得到通知並被自動更新

  2、模型結構:

    (1)抽象目標(Subject):提供了一個用於保存觀察者對象的聚集類和增加、刪除觀察者對象的方法,

                  以及通知所有觀察者的抽象方法

    (2)具體目標(Concrete Subject):它實現抽象目標中的通知方法,當具體目標的內部狀態發生改變時,

                        通知所有註冊過的觀察者對象

    (3)抽象觀察者(Observer):它是一個抽象類或介面,它包含了一個更新自己的抽象方法

                    當接到具體主題的更改通知時被調用

    (4)具體觀察者(Concrete Observer):實現抽象觀察者中定義的抽象方法

  3、優點:

    (1)降低了目標與觀察者之間的耦合關係,兩者之間是抽象耦合關係

    (2)目標與觀察者之間建立了一套觸發機制

  4、缺點:

    (1)目標與觀察者之間的依賴關係並沒有完全解除,而且有可能出現迴圈引用

    (2)當觀察者對象很多時,通知的發佈會花費很多時間,影響程式的效率

  5、適用環境:

    (1)對象間存在一對多關係,一個對象的狀態發生改變會影響其他對象

    (2)當一個抽象模型有兩個方面,其中一個方面依賴於另一方面時,

      可將這二者封裝在獨立的對象中以使它們可以各自獨立地改變和復用

// 抽象目標,抽象售賣火車票對象
abstract class Subject {
    protected observers: Observer[] = new Array<Observer>();
    // 增加觀察者
    add(observer: Observer): void {
        this.observers.push(observer);
    }
    // 刪除觀察者
    remove(observer: Observer): void {
        let idx = this.observers.indexOf(observer);
        // 若找到該觀察者,則刪除掉它
        if (idx > -1)
            this.observers.splice(idx, 1);
    }
    abstract notifyObserver(num: number): void;
}
// 具體目標,具體售賣火車票對象
class ConcreteSubjext extends Subject {
    private fare_num: number = 100;
    notifyObserver(num: number): void {
        for (let obs of this.observers) {
            this.fare_num = this.fare_num - num;
            obs.response(num, this.fare_num);
        }
    }
}
// 抽象觀察者
interface Observer {
    response(num: number, fare_num: number): void;
}
// 具體觀察者
class ConcreteObserver1 implements Observer {
    response(num: number, fare_num: number): void {
        console.log("------------------In obs1-----------------------");
        num > 0 ? console.log(`火車票賣出 ${num} 張`) : console.log(`火車票退回 ${-num} 張`);
        console.log(`剩餘 ${fare_num} 張票`);
    }
}
class ConcreteObserver2 implements Observer {
    response(num: number, fare_num: number): void {
        console.log("------------------In obs2-----------------------");
        num > 0 ? console.log(`火車票賣出 ${num} 張`) : console.log(`火車票退回 ${-num} 張`);
        console.log(`剩餘 ${fare_num} 張票`);
    }
}

let subject: Subject = new ConcreteSubjext();
let obs1: Observer = new ConcreteObserver1();
let obs2: Observer = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver(4);
subject.notifyObserver(-2);
subject.remove(obs1);
subject.notifyObserver(10);

 

狀態模式:

  1、定義:對有狀態的對象,把複雜的“判斷邏輯”提取到不同的狀態對象中,

        允許狀態對象在其內部狀態發生改變時改變其行為

  2、模型結構:

    (1)環境(Context):也稱為上下文,它定義了客戶感興趣的介面,維護一個當前狀態

                並將與狀態相關的操作委托給當前狀態對象來處理

    (2)抽象狀態(State):定義一個介面,用以封裝環境對象中的特定狀態所對應的行為

    (3)具體狀態(Concrete State):實現抽象狀態所對應的行為

  3、優點:

    (1)狀態模式將與特定狀態相關的行為局部化到一個狀態中,並將不同狀態的行為分割開,滿足“單一職責原則”

    (2)將不同的狀態引入獨立的對象中會使得狀態轉換變得更加明確,且減少對象間的相互依賴

    (3)有利於程式的擴展,通過定義新的子類很容易地增加新的狀態和轉換

  4、缺點:

    (1)狀態模式的使用必然會增加系統類和對象的個數

    (2)狀態模式的結構與實現都較為複雜,如果使用不當將導致程式結構和代碼的混亂

    (3)狀態模式對“開閉原則”的支持並不太好

  5、適用環境:

    (1)當一個對象的行為取決於它的狀態,並且它必須在運行時根據狀態改變它的行為時,就可以考慮使用狀態模式

    (2)一個操作中含有龐大的分支結構,並且這些分支決定於對象的狀態

// 環境類
class StateContext {
    private state: State;
    // 預設為 A 類,運行時先檢查符合哪個類
    constructor() {
        this.state = new ConcreteStateA();  
    }
    // 設置狀態
    setState(state: State): void {
        this.state = state;
    }
    // 獲取狀態
    getState(): State {
        return this.state;
    }
    // 處理輸入的成績
    Handle(grade: number): void {
        this.state.Handle(grade);
    }
}
// 抽象狀態類
abstract class State {
    protected context: StateContext;    // 環境對象
    protected stateName: string;    // 狀態名
    Handle(grade: number): void {
        this.check(grade);
        console.log(context.getState().stateName);
    };
    // 檢查成績符合哪個條件
    abstract check(grade: number): void;
}
class ConcreteStateA extends State {
    constructor() {
        super();
        this.stateName = "-----------------ConcreteStateA---------------";
    }
    check(grade: number): void {
        if (grade <= 50) {
            context.setState(new ConcreteStateB());
        }
    }
}
class ConcreteStateB extends State {
    constructor() {
        super();
        this.stateName = "-----------------ConcreteStateB---------------";
    }
    check(grade: number): void {
        if (grade > 50)
            context.setState(new ConcreteStateA());
    }
}

let context: StateContext = new StateContext();
context.Handle(90); // -----------------ConcreteStateA---------------
context.Handle(50); // -----------------ConcreteStateB---------------
context.Handle(70); // -----------------ConcreteStateA---------------
context.Handle(30); // -----------------ConcreteStateB---------------

 


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

-Advertisement-
Play Games
更多相關文章
  • 前言 在開發的時候,有時在命令工具裡面,要多開兩個視窗分別啟動前端項目和後端服務介面,有沒有辦法將整個項目一起啟動呢 答案是有,前端和後端連載一起啟動,適用於前端為vue或React,後端為nodejs的項目。 只需用到一個npm包concurrently模塊,通過package.json配置實現。 ...
  • 示例代碼托管在: "http://www.github.com/dashnowords/blogs" 博客園地址: "《大史住在大前端》原創博文目錄" 華為雲社區地址: "【你要的前端打怪升級指南】" [TOC] B站地址: "【編譯原理】" Stanford公開課: "【Stanford大學公開課 ...
  • 一.window相關 二.body相關 三.滾動相關 四.位置精確定位 五.坐標軸 六.width/clientWidth/offsetWidth以及height之間區別 ...
  • ## 今日內容 1. JQuery 基礎: 1. 概念 2. 快速入門 3. JQuery對象和JS對象區別與轉換 4. 選擇器 5. DOM操作 6. 案例 # JQuery 基礎: 1. 概念: 一個JavaScript框架。簡化JS開發 * jQuery是一個快速、簡潔的JavaScript框 ...
  • 1、複雜動畫 (1)涉及到的屬性: animation-name:動畫名稱; animation-duration:單次動畫總時長; animation-timing-function:時間函數; animation-delay:播放前延時的時長; animation-iteration-count ...
  • 1. 類型強制轉換 1.1 string強制轉換為數字 可以用 *1來轉化為數字(實際上是調用 .valueOf方法) 然後使用 Number.isNaN來判斷是否為 NaN,或者使用 a!==a 來判斷是否為 NaN,因為 NaN!==NaN 常用:也可以使用 +來轉化字元串為數字 1.2 obj ...
  • 下表列出了用於操作HTML和CSS的所有方法。 ...
  • 0--前言 對於分散式系統環境,主鍵ID的設計很關鍵,什麼自增intID那些是絕對不用的,比較早的時候,大部分系統都用UUID/GUID來作為主鍵,優點是方便又能解決問題,缺點是插入時因為UUID/GUID的不規則導致每插入一條數據就需要重新排列一次,性能低下;也有人提出用UUID/GUID轉lon ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...