責任鏈模式:
下圖為責任鏈

1、定義:為了避免請求發送者與多個請求處理者耦合在一起,將所有請求的處理者通過前一物件
記住其下一個物件的參考而連成一條鏈;當有請求發生時,可將請求沿著這條鏈傳遞,直到有物件處理它為止
2、模型結構:
(1)抽象處理者(Handler):定義一個處理請求的介面,包含抽象處理方法和一個后繼連接
(2)具體處理者(Concrete Handler):實作抽象處理者的處理方法,判斷能否處理本次請求,
如果可以處理請求則處理,否則將該請求轉給它的后繼者
3、優點:
(1)降低了物件之間的耦合度,該模式使得一個物件無須知道到底是哪一個物件處理其請求以及鏈的結構,
發送者和接收者也無須擁有對方的明確資訊
(2)增強了系統的可擴展性,可以根據需要增加新的請求處理類,滿足“開閉原則”
(3)增強了給物件指派職責的靈活性,當作業流程發生變化,可以動態地改變鏈內的成員或者調動它們的次序,
也可動態地新增或者洗掉責任
(4)簡化了物件之間的連接,每個物件只需保持一個指向其后繼者的參考,
不需保持其他所有處理者的參考,這避免了使用眾多的分支陳述句
(5)責任分擔,每個類只需要處理自己該處理的作業,不該處理的傳遞給下一個物件完成,
明確各類的責任范圍,符合類的單一職責原則
注:單一職責原則規定一個類應該有且僅有一個引起它變化的原因,否則類應該被拆分
4、缺點:
(1)不能保證每個請求一定被處理,由于一個請求沒有明確的接收者,
所以不能保證它一定會被處理,該請求可能一直傳到鏈的末端都得不到處理
(2)對比較長的職責鏈,請求的處理可能涉及多個處理物件,系統性能將受到一定影響
(3)職責鏈建立的合理性要靠客戶端來保證,增加了客戶端的復雜性,
可能會由于職責鏈的錯誤設定而導致系統出錯,如可能會造成回圈呼叫
5、適用環境:
(1)有多個物件可以處理一個請求,哪個物件處理該請求由運行時刻自動確定
(2)可動態指定一組物件處理請求,或添加新的處理者
(3)在不明確指定請求處理者的情況下,向多個處理者中的一個提交請求
// 抽象處理者 Handlerabstract class ATeacher { // 鏈式結構,需要包含一個指向下一個 Handler 的物件 protected aTeacher: ATeacher; setTeacher(teacher: ATeacher): void { this.aTeacher = teacher; } getTeacher(): ATeacher { return this.aTeacher; } abstract handler(level: number): void;}// 具體處理者 Concrete Handlerclass Teacher extends ATeacher { private name: string; private something: string; constructor(name: string, something: string) { super(); // Constructors for derived classes must contain a 'super' call this.name = name; this.something = something } handler(level: number): void { if (level >= 3) { console.log("授課老師同意..."); } else { console.log("授課老師無權受理..."); this.getTeacher().handler(level); } }}class Moniteur extends ATeacher { private name: string; private something: string; constructor(name: string, something: string) { super(); // Constructors for derived classes must contain a 'super' call this.name = name; this.something = something } handler(level: number): void { let flag: boolean = true; // 設定為同意 if (level >= 2) { if (flag) console.log("輔導員同意..."); else console.log("輔導員不同意..."); } else { console.log("輔導員無權受理..."); this.getTeacher().handler(level); } }}class Directeur extends ATeacher { private name: string; private something: string; constructor(name: string, something: string) { super(); // Constructors for derived classes must contain a 'super' call this.name = name; this.something = something } handler(level: number): void { let flag: boolean = false; // 設定為不同意 if (level >= 1) { if (flag) console.log("主任同意..."); else console.log("主任不同意..."); } else { console.log("主任無權受理..."); this.getTeacher().handler(level); } }}let stuName1: string = "Lemon";let thing: string = "Go home";let teacher1: ATeacher = new Teacher(stuName1, thing);let teacher2: ATeacher = new Moniteur(stuName1, thing);let teacher3: ATeacher = new Directeur(stuName1, thing);teacher1.setTeacher(teacher2);teacher2.setTeacher(teacher3);// 授課老師無權受理...// 輔導員同意...teacher1.handler(2);
命令模式:

1、定義:將一個請求封裝為一個物件,使發出請求的責任和執行請求的責任分割開,
這樣兩者之間通過命令物件進行溝通,這樣方便將命令物件進行儲存、傳遞、呼叫、增加與管理
2、模型結構:
(1)抽象命令類(Command):宣告執行命令的介面,擁有執行命令的抽象方法
(2)具體命令類(ConcreteCommand):是抽象命令類的具體實作類,它擁有接收者物件,
并通過呼叫接收者的功能來完成命令要執行的操作
(3)呼叫者(Invoker):請求的發送者,它通常擁有很多的命令物件,
并通過訪問命令物件來執行相關請求,不直接訪問接收者
(4)接收者(Receiver):執行命令功能的相關操作,是具體命令物件業務的真正實作者
3、優點:
(1)降低系統的耦合度:命令模式能將呼叫操作的物件與實作該操作的物件解耦
(2)增加或洗掉命令非常方便:采用命令模式增加與洗掉命令不會影響其他類,滿足“開閉原則”,擴展比較靈活
(3)可以實作宏命令:命令模式可以與組合模式結合,將多個命令裝配成一個組合命令,即宏命令
(4)方便實作 Undo 和 Redo 操作:命令模式可以與后面介紹的備忘錄模式結合,實作命令的撤銷與恢復
4、缺點:可能產生大量具體命令類,因為計對每一個具體操作都需要設計一個具體命令類,這將增加系統的復雜性
5、適用環境:
(1)系統需要將請求呼叫者和請求接收者解耦,使得呼叫者和接收者不直接互動
(2)系統需要在不同的時間指定請求、將請求排隊和執行請求
(3)系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作
(4)系統需要將一組操作組合在一起,即支持宏命令
// 接收者 Receiverclass RStudent { clean(name: string): void { console.log(name + " begin cleaning..."); } doHomwork(name: string) { console.log(name + " begin doing homework..."); }}// 抽象命令類 Commandabstract class Command { protected student: RStudent; constructor(student: RStudent) { this.student = student; } abstract execute(name: string): void;}// 具體命令類 ConcreteCommandclass LiTeacher extends Command { constructor(student: RStudent) { super(student); } execute(name: string): void { this.student.clean(name); }}class WangTeacher extends Command { constructor(student: RStudent) { super(student); } execute(name: string): void { this.student.doHomwork(name); }}// 呼叫者 Invokerclass Invoker { protected command: Command; commands: Command[] = new Array<Command>(); executeCommand(name: string): void { this.command = this.commands.shift(); this.command.execute(name); console.log("Executing one command..."); } setCommand(command: Command): void { console.log(`Here are ${this.commands.length} things must be executed before execute this`); this.commands.push(command); } undoCommand(): void { if (this.commands.length) { this.commands.pop(); console.log("Undo command is successful!"); } else { console.log("You shouldn't execute any command after you set it if you want to undo it..."); } }}let stuName: string = "Tim";let stu: RStudent = new RStudent();let command1: LiTeacher = new LiTeacher(stu);let command2: WangTeacher = new WangTeacher(stu);let invoker: Invoker = new Invoker();invoker.setCommand(command1); // Here are 0 things must be executed before execute thisinvoker.setCommand(command2); // Here are 1 things must be executed before execute thisinvoker.executeCommand(stuName); // Tim begin cleaning... \n Executing one command...invoker.setCommand(command1); // Here are 1 things must be executed before execute thisinvoker.undoCommand(); // Undo command is successful!invoker.executeCommand(stuName); // Tim begin doing homework... \n Executing one command...
解釋器模式:

1、定義:給分析物件定義一個語言,并定義該語言的文法表示,再設計一個決議器來解釋語言中的句子
注:文法是用于描述語言的語法結構的形式規則
2、模型結構:
(1)抽象運算式(Abstract Expression):定義解釋器的介面,約定解釋器的解釋操作
(2)終結符運算式(Terminal Expression):抽象運算式的子類,用來實作文法中與終結符相關的操作,
文法中的每一個終結符都有一個具體終結運算式與之相對應
(3)非終結符運算式(Nonterminal Expression):抽象運算式的子類,用來實作文法中與非終結符相關的操作,
文法中的每條規則都對應于一個非終結符運算式
(4)環境(Context):通常包含各個解釋器需要的資料或是公共的功能,一般用來傳遞被所有解釋器共享的資料,
后面的解釋器可以從這里獲取這些值
3、優點:
(1)擴展性好:由于在解釋器模式中使用類來表示語言的文法規則,因此可以通過繼承等機制來改變或擴展文法
(2)容易實作:在語法樹中的每個運算式節點類都是相似的,所以實作其文法較為容易
注:語法樹是句子結構的一種樹型表示,它代表了句子的推導結果,它有利于理解句子語法結構的層次
4、缺點:
(1)執行效率較低:解釋器模式中通常使用大量的回圈和遞回呼叫,當要解釋的句子較復雜時,
其運行速度很慢,且代碼的除錯程序也比較麻煩
(2)引起類膨脹:解釋器模式中的每條規則至少需要定義一個類,當包含的文法規則很多時,
類的個數將急劇增加,導致系統難以管理與維護
(3)可應用的場景比較少:在軟體開發中,需要定義語言文法的應用實體非常少,所以這種模式很少被使用到
5、適用環境:
(1)當語言的文法較為簡單,且執行效率不是關鍵問題時
(2)當問題重復出現,且可以用一種簡單的語言來進行表達時
(3)當一個語言需要解釋執行,并且語言中的句子可以表示為一個抽象語法樹的時候,如 XML 檔案解釋
// 抽象運算式interface AbstractExpression { interpret(age: string, occupation: string): boolean;}// 終結符運算式class TerminalExpression implements AbstractExpression { private set: Set<string> = new Set<string>(); constructor(data: string[]) { for (let i in data) { this.set.add(data[i]); } } interpret(age: string, occupation: string): boolean { if (Number(age) <= 12 || this.set.has(occupation)) { return true; } return false; }}// 非終結符運算式class AndExpression implements AbstractExpression { private people: AbstractExpression = null; constructor(people: AbstractExpression) { this.people = people; } interpret(age: string, occupation: string): boolean { return this.people.interpret(age, occupation); }}// 環境類class Context { private occupations: string[] = ["學生", "老人", "孕婦"]; private people: AbstractExpression; constructor() { let occupation: AbstractExpression = new TerminalExpression(this.occupations); this.people = new AndExpression(occupation); } halfPrice(info: string) { let info_arr: string[] = info.split("歲"); let ok: boolean = this.people.interpret(info_arr[0], info_arr[1]); if (ok) { console.log(`您是${info},本次費用半價...`); } else { console.log(`${info},您不是優惠人群,本次費用全票...`); } }}let sp: Context = new Context();sp.halfPrice("12歲學生"); // 您是12歲學生,本次費用半價...sp.halfPrice("8歲兒童"); // 您是8歲兒童,本次費用半價...sp.halfPrice("20歲學生"); // 您是20歲學生,本次費用半價...sp.halfPrice("68歲老人"); // 您是68歲老人,本次費用半價...sp.halfPrice("28歲孕婦"); // 您是28歲孕婦,本次費用半價...sp.halfPrice("18歲無業游民"); // 18歲無業游民,您不是優惠人群,本次費用全票...
迭代器模式:

1、定義:提供一個物件來順序訪問聚合物件中的一系列資料,而不暴露聚合物件的內部表示
2、模型結構:
(1)抽象聚合(Aggregate):定義存盤、添加、洗掉聚合物件以及創建迭代器物件的介面
(2)具體聚合(ConcreteAggregate):實作抽象聚合類,回傳一個具體迭代器的實體
(3)抽象迭代器Iterator:定義訪問和遍歷聚合元素的介面,通常包含 hasNext()、first()、next() 等方法
(4)具體迭代器(Concretelterator):實作抽象迭代器介面中所定義的方法,完成對聚合物件的遍歷,
記錄遍歷的當前位置
3、優點:
(1)訪問一個聚合物件的內容而無須暴露它的內部表示
(2)遍歷任務交由迭代器完成,這簡化了聚合類
(3)它支持以不同方式遍歷一個聚合,甚至可以自定義迭代器的子類以支持新的遍歷
(4)增加新的聚合類和迭代器類都很方便,無須修改原有代碼
(5)封裝性良好,為遍歷不同的聚合結構提供一個統一的介面
4、缺點:增加了類的個數,這在一定程度上增加了系統的復雜性
5、適用環境:
(1)當需要為聚合物件提供多種遍歷方式時
(2)當需要為遍歷不同的聚合結構提供一個統一的介面時
(3)當訪問一個聚合物件的內容而無須暴露其內部細節的表示時
// 學校類,用于聚合class School { private schoolName: string; constructor(schoolName: string) { this.schoolName = schoolName; } printName(): void { console.log(this.schoolName); }}// 抽象聚合interface Aggregate { add(school: School): void; remove(): School; getIterator(): MyIterator;}// 具體聚合class ConcreteAggregate implements Aggregate { private schoolList: School[] = new Array<School>(); add(school: School): void { this.schoolList.push(school); } remove(): School { let temp: School = this.schoolList.pop(); return temp; } getIterator(): MyIterator { return new ConcreteIterator(this.schoolList); }}// 抽象迭代器interface MyIterator { first(): School; next(): School; hasNext(): boolean; reset(): void;}// 具體迭代器class ConcreteIterator implements MyIterator { private schoolList: School[]; private index: number = -1; constructor(schoolList: School[]) { this.schoolList = schoolList; } first(): School { let first: School = null; if (this.schoolList.length > 0) first = this.schoolList[0]; return first; } next(): School { let next: School = null; if (this.hasNext()) // index 初始值為 -1,所以需要先加 next = this.schoolList[++this.index]; return next; } hasNext(): boolean { if (this.index < this.schoolList.length-1) return true; return false; } reset(): void { this.index = -1; }}let school1: School = new School("華南師范大學");let school2: School = new School("北京郵電大學");let school3: School = new School("東南大學");let schools: Aggregate = new ConcreteAggregate();schools.add(school1);schools.add(school2);schools.add(school3);let it: MyIterator = schools.getIterator();while (it.hasNext()) { it.next().printName(); // 依次輸出學校名字}it.first().printName(); // 華南師范大學console.log(it.hasNext()); // false,因為 index 已經到末尾了it.reset(); // 重置 indexconsole.log(it.hasNext()); // trueschools.remove().printName(); // 東南大學
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/42127.html
標籤:設計模式
上一篇:設計模式-行為型-狀態模式
下一篇:設計模式-行為型-觀察者模式
