做者簡介 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('####', '@'),歡迎有志之士加入~