責任鏈模式: 下圖為責任鏈 1、定義:為了避免請求發送者與多個請求處理者耦合在一起,將所有請求的處理者通過前一對象 記住其下一個對象的引用而連成一條鏈;當有請求發生時,可將請求沿著這條鏈傳遞,直到有對象處理它為止 2、模型結構: (1)抽象處理者(Handler):定義一個處理請求的介面,包含抽象處 ...
責任鏈模式:
下圖為責任鏈
1、定義:為了避免請求發送者與多個請求處理者耦合在一起,將所有請求的處理者通過前一對象
記住其下一個對象的引用而連成一條鏈;當有請求發生時,可將請求沿著這條鏈傳遞,直到有對象處理它為止
2、模型結構:
(1)抽象處理者(Handler):定義一個處理請求的介面,包含抽象處理方法和一個後繼連接
(2)具體處理者(Concrete Handler):實現抽象處理者的處理方法,判斷能否處理本次請求,
如果可以處理請求則處理,否則將該請求轉給它的後繼者
3、優點:
(1)降低了對象之間的耦合度。該模式使得一個對象無須知道到底是哪一個對象處理其請求以及鏈的結構,
發送者和接收者也無須擁有對方的明確信息
(2)增強了系統的可擴展性。可以根據需要增加新的請求處理類,滿足“開閉原則”
(3)增強了給對象指派職責的靈活性。當工作流程發生變化,可以動態地改變鏈內的成員或者調動它們的次序,
也可動態地新增或者刪除責任
(4)簡化了對象之間的連接。每個對象只需保持一個指向其後繼者的引用,
不需保持其他所有處理者的引用,這避免了使用眾多的分支語句
(5)責任分擔。每個類只需要處理自己該處理的工作,不該處理的傳遞給下一個對象完成,
明確各類的責任範圍,符合類的單一職責原則
註:單一職責原則規定一個類應該有且僅有一個引起它變化的原因,否則類應該被拆分
4、缺點:
(1)不能保證每個請求一定被處理。由於一個請求沒有明確的接收者,
所以不能保證它一定會被處理,該請求可能一直傳到鏈的末端都得不到處理
(2)對比較長的職責鏈,請求的處理可能涉及多個處理對象,系統性能將受到一定影響
(3)職責鏈建立的合理性要靠客戶端來保證,增加了客戶端的複雜性,
可能會由於職責鏈的錯誤設置而導致系統出錯,如可能會造成迴圈調用
5、適用環境:
(1)有多個對象可以處理一個請求,哪個對象處理該請求由運行時刻自動確定
(2)可動態指定一組對象處理請求,或添加新的處理者
(3)在不明確指定請求處理者的情況下,向多個處理者中的一個提交請求
// 抽象處理者 Handler abstract class ATeacher { // 鏈式結構,需要包含一個指向下一個 Handler 的對象 protected aTeacher: ATeacher; setTeacher(teacher: ATeacher): void { this.aTeacher = teacher; } getTeacher(): ATeacher { return this.aTeacher; } abstract handler(level: number): void; } // 具體處理者 Concrete Handler class 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)系統需要將一組操作組合在一起,即支持巨集命令
// 接收者 Receiver class RStudent { clean(name: string): void { console.log(name + " begin cleaning..."); } doHomwork(name: string) { console.log(name + " begin doing homework..."); } } // 抽象命令類 Command abstract class Command { protected student: RStudent; constructor(student: RStudent) { this.student = student; } abstract execute(name: string): void; } // 具體命令類 ConcreteCommand class 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); } } // 調用者 Invoker class 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 this invoker.setCommand(command2); // Here are 1 things must be executed before execute this invoker.executeCommand(stuName); // Tim begin cleaning... \n Executing one command... invoker.setCommand(command1); // Here are 1 things must be executed before execute this invoker.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(); // 重置 index console.log(it.hasNext()); // true schools.remove().printName(); // 東南大學