做者简介 joey 蚂蚁金服·数据体验技术团队html
继前面几篇设计模式文章以后,这篇介绍5个对象行为型设计模式。java
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。git
职责链模式包含以下角色:github
interface RequestData {
name: string,
increaseNum: number,
}
/**
* 抽象处理者
*/
abstract class Handler {
protected next: Handler;
setNext(next: Handler) {
this.next = next;
}
abstract processRequest(request: RequestData): void;
}
class IdentityValidator extends Handler {
processRequest(request: RequestData) {
if (request.name === 'yuanfeng') {
console.log(`${request.name} 是本公司的员工`);
this.next.processRequest(request);
} else {
console.log('不是本公司员工');
}
}
}
class Manager extends Handler {
processRequest(request: RequestData) {
if (request.increaseNum < 300) {
console.log('低于300的涨薪,经理直接批准了');
} else {
console.log(`${request.name}的涨薪要求超过了经理的权限,须要更高级别审批`);
this.next.processRequest(request);
}
}
}
class Boss extends Handler {
processRequest(request: RequestData) {
console.log('hehe,想涨薪,你能够走了');
}
}
function chainOfResponsibilityDemo() {
const identityValidator = new IdentityValidator();
const manager = new Manager();
const boss = new Boss();
// 构建职责链
identityValidator.setNext(manager);
manager.setNext(boss);
const request: RequestData = {
name: 'yuanfeng',
increaseNum: 500,
};
identityValidator.processRequest(request);
}
chainOfResponsibilityDemo();
复制代码
将一个请求封装为一个对象,从而使你可用不一样的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操做。编程
命名模式包含如下角色:设计模式
// 点菜场景下,客户点餐后彻底不须要知道作菜的厨师是谁,记载着客户点菜信息的订单就是一个命令。
// 命令的基类,只包含了一个执行方法
class Command {
execute(arg?): void {}
}
// 厨师类,每一个厨师都会作面包和肉
class Cook {
private name: string;
constructor(name: string) {
this.name = name;
}
makeBread() {
console.log(`厨师 ${this.name} 在作面包`);
}
makeMeal() {
console.log(`厨师 ${this.name} 在作肉`);
}
}
// 简单命令只须要包含接收者和执行接口
class SimpleCommand extends Command {
// 接收者,在点菜系统里是厨师
receiver: Cook;
}
// 作面包的命令类
class BreadCommand extends SimpleCommand {
constructor(cook: Cook) {
super();
this.receiver = cook;
}
execute() {
this.receiver.makeBread();
}
}
// 作肉的命令类
class MealCommand extends SimpleCommand {
constructor(cook: Cook) {
super();
this.receiver = cook;
}
execute() {
this.receiver.makeMeal();
}
}
// 系统启动时,将命令注册到菜单上,生成可被处处使用的命令对象
function simpleCommandDemo(): void {
const cook1 = new Cook('厨师1');
const cook2 = new Cook('厨师2');
// 生成菜单,上架销售,顾客能够选择点肉或点面包
const breadCommand: Command = new BreadCommand(cook1);
const mealCommand: Command = new MealCommand(cook2);
// 客户点菜时,彻底不须要知道是哪一个厨师作的,只须要从菜单上点想要的菜,即下命令便可
// 此时已经作到了命令的触发者与接收者的分离
// 命令对象能够在整个系统中处处传递,如通过多个服务员,而不会丢失接受者的信息
breadCommand.execute();
mealCommand.execute();
}
复制代码
相比简单命令,除了在命令对象中保存了接收者,还须要存储额外的状态信息,如接收者上次执行操做的参数数组
class AdvancedCommand extends Command {
// 接收者
ball: Ball;
// 额外状态信息,移动的距离
pos: number;
// 执行命令时候,向左移动,同时记录下移动的距离
execute(pos: number) {
this.pos = pos;
this.ball.moveToLeft(pos);
}
// 撤销时执行反向操做
unExecute() {
this.ball.moveToRight(this.pos);
}
}
复制代码
同时容许多个命令,这里不须要显式的接收者,由于每一个命令都已经定义了各自的接收者bash
class MacroCommand extends Command {
// 保存命令列表
cmdSet: Set<Command> = [];
add(cmd: Command): void {
this.cmdSet.add(cmd);
}
remove(cmd: Command): void {
this.cmdSet.delete(cmd);
}
execute(): void {
this.cmdSet.forEach((cmd: Command) => {
cmd.execute();
});
}
}
复制代码
提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。编程语言
迭代器模式包含如下角色:ide
相对于迭代器模式的经典结构,简化了实现,去除了抽象聚合类和具体聚合类的设计,同时简化了迭代器接口。
// 迭代器接口
interface Iterator {
next(): any;
first(): any;
isDone(): boolean;
}
// 顺序挨个遍历数组的迭代器
class ListIterator implements Iterator {
protected list: Array<any> = [];
protected index: number = 0;
constructor(list) {
this.list = list;
}
first() {
if (this.list.length) {
return this.list[0];
}
return null;
}
next(): any {
if (this.index < this.list.length) {
this.index += 1;
return this.list[this.index];
}
return null;
}
isDone(): boolean {
return this.index >= this.list.length;
}
}
// 跳着遍历数组的迭代器
// 因为跳着遍历和逐个遍历,区别只在于next方法,所以经过继承简单实现
class SkipIterator extends ListIterator {
next(): any {
if (this.index < this.list.length) {
const nextIndex = this.index + 2;
if (nextIndex < this.list.length) {
this.index = nextIndex;
return this.list[nextIndex];
}
}
return null;
}
}
// 对同一个序列,调用不一样的迭代器就能实现不一样的遍历方式,而不须要将迭代方法写死在序列中
// 经过迭代器的方式,将序列与遍历方法分离
function iteratorDemo(): void {
const list = [1,2,3,4,5,6];
// 挨个遍历
const listIterator: Iterator = new ListIterator(list);
while(!listIterator.isDone()) {
const item: number = listIterator.next();
console.log(item);
}
// 跳着遍历
const skipIterator: Iterator = new SkipIterator(list);
while(!listIterator.isDone()) {
const item: number = skipIterator.next();
console.log(item);
}
}
// 内部迭代器,即在聚合内部定义的迭代器,外部调用不须要关心迭代器的具体实现,缺点是功能被固定,不易扩展
class SkipList {
list = [];
constructor(list: Array<any>) {
this.list = list;
}
// 内部定义了遍历的规则
// 这里实现为间隔遍历
loop(callback) {
if (this.list.length) {
let index = 0;
const nextIndex = index + 2;
if (nextIndex < this.list.length) {
callback(this.list[nextIndex]);
index = nextIndex;
}
}
}
}
function innerIteratorDemo(): void {
const list = [1,2,3,4,5,6];
const skipList = new SkipList(list);
// 按照聚合的内部迭代器定义的规则迭代
skipList.loop(item => {
console.log(item);
});
}
复制代码
用一个中介对象来封装一系列的对象交互,中介者使各对象不须要显式地相互引用,从而使其耦合松散,并且能够独立地改变它们之间的交互。
中介者模式包含如下角色:
租房的案例,租客和房主经过中介者联系,二者并不直接联系
// 抽象中介者
abstract class Mediator {
abstract contact(message: string, person: Human): void
}
// 抽象同事类
abstract class Human {
name: string
mediator: Mediator
constructor(name: string, mediator: Mediator) {
this.name = name;
this.mediator = mediator;
}
}
// 2个具体的同事类
// 房主类
class HouseOwner extends Human {
contact(message: string) {
console.log(`房主 ${this.name} 发送消息 ${message}`);
this.mediator.contact(message, this);
}
getMessage(message: string) {
console.log(`房主 ${this.name} 收到消息 ${message}`);
}
}
// 租客类
class Tenant extends Human {
contact(message: string) {
console.log(`租客 ${this.name} 发送消息 ${message}`);
this.mediator.contact(message, this);
}
getMessage(message: string) {
console.log(`租客 ${this.name} 收到消息 ${message}`);
}
}
// 具体中介者
class ConcreteMediator extends Mediator {
private tenant: Tenant;
private houseOwner: HouseOwner;
setTenant(tenant: Tenant) {
this.tenant = tenant;
}
setHouseOwner(houseOwner: HouseOwner) {
this.houseOwner = houseOwner;
}
// 由中介者来设置同事对象之间的联系关系
contact(message: string, person: Human) {
console.log('中介传递消息');
if (person === this.houseOwner) {
this.tenant.getMessage(message);
} else {
this.houseOwner.getMessage(message);
}
}
}
function mediatorDemo() {
const mediator = new ConcreteMediator();
const houseOwner = new HouseOwner('财大气粗的房叔', mediator);
const tenant = new Tenant('远峰', mediator);
// 向中介者注册成员
mediator.setHouseOwner(houseOwner);
mediator.setTenant(tenant);
// 中介的成员只须要发送信息,而不须要关心具体接受者,联系关系都维护在了中介者中
tenant.contact('我想租房');
houseOwner.contact('我有房,你要租吗');
}
复制代码
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象以外保存这个状态。这样之后就可将该对象恢复到原先保存的状态。
备忘录模式包含如下角色:
案例:一个角色在画布中移动
// 备忘录类
class Memento {
private x: number;
private y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
getX(): number {
return this.x;
}
getY(): number {
return this.y;
}
}
// 原发器类
class Role {
private x: number;
private y: number;
constructor(name: string, x: number, y: number) {
this.x = x;
this.y = y;
}
// 移动到新的位置
moveTo(x: number, y: number): Memento {
this.x = x;
this.y = y;
return this.save();
}
save(): Memento {
return new Memento(this.x, this.y);
}
// 根据备忘录回退到某一个位置
goBack(memento: Memento) {
this.x = memento.getX();
this.y = memento.getY();
}
}
// 负责人,管理全部备忘录
class HistoryRecords {
private records = [];
// 添加备忘录
add(record: Memento): void {
this.records.push(record);
}
// 返回备忘录
get(index: number): Memento {
if (this.records[index]) {
return this.records[index];
}
return null;
}
// 清除指定位置后面的备忘录
cleanRecordsAfter(index: number): void {
this.records.slice(0, index + 1);
}
}
// 客户代码
function mementoDemo() {
const role = new Role('卡通小人', 0, 0);
const records = new HistoryRecords();
// 记录初始位置
records.add(role.save());
// 移动时添加备忘录
role.moveTo(10, 10);
records.add(role.save());
role.moveTo(20, 30);
records.add(role.save());
// 回退到初始位置
const GO_BACK_STEP = 0;
const firstMemento = records.get(GO_BACK_STEP);
role.goBack(firstMemento);
// 清除后面的记录
records.cleanRecordsAfter(GO_BACK_STEP);
}
复制代码
本文介绍了5种对象行为型模式,对后续模式感兴趣的同窗能够关注专栏或者发送简历至'tao.qit####alibaba-inc.com'.replace('####', '@'),欢迎有志之士加入~