中介者模式: 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---------------