行为型设计模式(中)
中介者模式:
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)状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱
5、适用环境:
(1)当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式
(2)一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时
// 环境类 class StateContext { private state: State; private grade: number; // 默认为 A 类,运行时先检查符合哪个类 constructor() { this.state = new ConcreteStateA(); } // 设置状态 setState(): void { if (this.grade > 50) this.state = new ConcreteStateA(); else if (this.grade <= 50) this.state = new ConcreteStateB(); } // 获取状态 getState(): State { return this.state; } // 处理输入的成绩 Handle(grade: number): void { this.grade = grade; this.setState(); this.state.Handle(); } } // 抽象状态类 abstract class State { protected context: StateContext; // 环境对象 protected stateName: string; // 状态名 Handle(): void { console.log(context.getState().stateName); }; } class ConcreteStateA extends State { constructor() { super(); this.stateName = "-----------------ConcreteStateA---------------"; } } class ConcreteStateB extends State { constructor() { super(); this.stateName = "-----------------ConcreteStateB---------------"; } } let context: StateContext = new StateContext(); context.Handle(90); // -----------------ConcreteStateA--------------- context.Handle(50); // -----------------ConcreteStateB--------------- context.Handle(70); // -----------------ConcreteStateA--------------- context.Handle(30); // -----------------ConcreteStateB---------------