设计模式是在软件开发中针对常见问题总结出来的解决方案。它们不是代码,而是一些经过验证的设计思想和最佳实践。下面是对每种模式的概述和实现:
策略模式
定义算法族,并将它们分别封装起来,让它们之间可以相互替换。
// 策略模式
interface PaymentMethod {
pay(amount: number): void;
}
class WeChat implements PaymentMethod {
pay(amount: number): void {
console.log(`Pay ${amount} via WeChat`);
}
}
class Alipay implements PaymentMethod {
pay(amount: number): void {
console.log(`Pay ${amount} via Alipay`);
}
}
class Credit implements PaymentMethod {
pay(amount: number): void {
console.log(`Pay ${amount} via Credit`);
}
}
class MTWallet implements PaymentMethod {
pay(amount: number): void {
console.log(`Pay ${amount} via MT wallet`);
}
}
class ELMWallet implements PaymentMethod {
pay(amount: number): void {
console.log(`Pay ${amount} via ELM wallet`);
}
}
abstract class TaskAway {
private paymentMethod: PaymentMethod | undefined;
constructor(method?: PaymentMethod) {
this.paymentMethod = method;
}
pay(amount: number) {
if (!this.paymentMethod) {
throw new Error('No payment method specified');
}
this.paymentMethod.pay(amount);
}
selectPaymentMethod(method: PaymentMethod) {
this.paymentMethod = method;
return this;
}
abstract pickFood(): void;
}
class MT extends TaskAway {
constructor() {
super(new MTWallet());
}
pickFood(): void {
console.log('pick food on the MT platform');
}
}
class ELM extends TaskAway {
constructor() {
super(new ELMWallet());
}
pickFood(): void {
console.log('pick food on the ELM platform');
}
}
const mt = new MT();
mt.pay(9.9);
mt.selectPaymentMethod(new WeChat()).pay(18.3);
const elm = new MT();
elm.selectPaymentMethod(new Alipay()).pay(29.9);
观察者模式
定义对象之间的一对多依赖,当对象状态变更时,它的依赖者们都会收到通知。
// 观察者模式
interface Listener {
(...args: any[]): void;
}
class MyEventEmitter {
private listenersGroupByEventName: { [key: string]: Listener[] };
constructor() {
this.listenersGroupByEventName = {};
}
on(eventName: string, listener: Listener): boolean {
if (this.listenersGroupByEventName[eventName]) {
this.listenersGroupByEventName[eventName].push(listener);
} else {
this.listenersGroupByEventName[eventName] = [listener];
}
return true;
}
off(eventName: string, listener: Listener): boolean {
if (!this.listenersGroupByEventName[eventName]) {
return false;
}
const index = this.listenersGroupByEventName[eventName].findIndex((cb) => cb === listener);
if (index < 0) {
return false;
}
this.listenersGroupByEventName[eventName].splice(index, 1);
return true;
}
emit(eventName: string, ...args: any[]): void {
if (this.listenersGroupByEventName[eventName]) {
this.listenersGroupByEventName[eventName].forEach((listener) => listener(...args));
}
}
}
const myEmitter = new MyEventEmitter();
const listener1 = (...args: any[]) => console.log(`listener1 received: ${args}`);
const listener2 = (...args: any[]) => console.log(`listener2 received: ${args}`);
myEmitter.on('en1', listener1);
myEmitter.on('en1', listener2);
myEmitter.on('en2', listener1);
myEmitter.emit('en1', 1, 2, 3);
myEmitter.emit('en2', 4, 5, 6);
myEmitter.emit('not Exists', 7, 8, 9);
myEmitter.off('en1', listener2);
myEmitter.emit('en1', 'hello world');
简单工厂
不是一种设计模式,但是比较常用。
// 简单工厂写法
abstract class MessageDelegate {
formatContent(content: string): string {
return content;
}
abstract sendMessage(toUserId: string, content: string): void;
}
class SMS extends MessageDelegate {
sendMessage(toUserId: string, text: string): void {
console.log(`Send sms to ${toUserId}: ${text}`);
}
}
class Email extends MessageDelegate {
formatContent(content: string): string {
return `<b>${content}</b>`
}
sendMessage(toUserId: string, body: string): void {
console.log(`Send email to ${toUserId}: ${body}`);
}
}
class SimpleMessageDelegateFactory {
static createMessageDelegate(messagePlatform: string): MessageDelegate {
switch (messagePlatform) {
case 'sms':
return new SMS();
case 'email':
return new Email();
default:
throw new Error('Unknown message platform')
}
}
}
class BroadCast {
send(messagePlatform: string, toUserId: string, messageContent: string): void {
const messageDelegate = SimpleMessageDelegateFactory.createMessageDelegate(messagePlatform);
const formattedContent = messageDelegate.formatContent(messageContent);
messageDelegate.sendMessage(toUserId, formattedContent);
}
}
const b = new BroadCast();
b.send('sms', '10086', '1');
b.send('email', '1234@qq.com', 'Apply for NodeJs Development Engineer');
工厂模式
定义了一个创建接口,但由子类具体决定要实例化的类是哪一个。
相比于简单工厂,可以通过继承Broadcast类,重载send函数执行逻辑。
// 工厂模式
abstract class MessageDelegate {
formatContent(content: string): string {
return content;
}
abstract sendMessage(toUserId: string, content: string): void;
}
class SMS extends MessageDelegate {
sendMessage(toUserId: string, text: string): void {
console.log(`Send sms to ${toUserId}: ${text}`);
}
}
class Email extends MessageDelegate {
formatContent(content: string): string {
return `<b>${content}</b>`
}
sendMessage(toUserId: string, body: string): void {
console.log(`Send email to ${toUserId}: ${body}`);
}
}
class BroadCast {
send(messagePlatform: string, toUserId: string, messageContent: string): void {
const messageDelegate = this.createMessageDelegate(messagePlatform);
const formattedContent = messageDelegate.formatContent(messageContent);
messageDelegate.sendMessage(toUserId, formattedContent);
}
createMessageDelegate(messagePlatform: string): MessageDelegate {
switch (messagePlatform) {
case 'sms':
return new SMS();
case 'email':
return new Email();
default:
throw new Error('Unknown message platform')
}
}
}
class BroadCastWithLogTracer extends BroadCast {
send(messagePlatform: string, toUserId: string, messageContent: string): void {
console.info(`Start sending ${messagePlatform}...`);
const messageDelegate = this.createMessageDelegate(messagePlatform);
const formattedContent = messageDelegate.formatContent(messageContent);
messageDelegate.sendMessage(toUserId, formattedContent);
console.info(`Send ${messagePlatform} completed!!!`);
}
}
const b = new BroadCastWithLogTracer();
b.send('sms', '10086', '1');
b.send('email', '1234@qq.com', 'Apply for NodeJs Development Engineer');
抽象工厂模式
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
// 抽象工厂模式
interface Button {
render(): void;
}
interface Input {
render(): void;
}
interface LoginUIFactory {
createLoginButton(): Button;
createAccountInput(): Input;
createPasswordInput(): Input;
}
class AndroidLoginButton implements Button {
render(): void {
console.log('Render a android style login button');
}
}
class IOSLoginButton implements Button {
render(): void {
console.log('Render a ios style login button');
}
}
class AndroidAccountInput implements Input {
render(): void {
console.log('Render a android style account input');
}
}
class IOSAccountInput implements Input {
render(): void {
console.log('Render a ios style account input');
}
}
class AndroidPasswordInput implements Input {
render(): void {
console.log('Render a android style password input');
}
}
class IOSPasswordInput implements Input {
render(): void {
console.log('Render a ios style password input');
}
}
class AndroidLoginUIFactory implements LoginUIFactory {
createLoginButton(): Button {
return new AndroidLoginButton();
}
createAccountInput(): Input {
return new AndroidAccountInput();
}
createPasswordInput(): Input {
return new AndroidPasswordInput();
}
}
class IOSLoginUIFactory implements LoginUIFactory {
createLoginButton(): Button {
return new IOSLoginButton();
}
createAccountInput(): Input {
return new IOSAccountInput();
}
createPasswordInput(): Input {
return new IOSPasswordInput();
}
}
class LoginPage {
private loginButton: Button;
private accountInput: Input;
private passwordInput: Input;
constructor(factory: LoginUIFactory) {
this.loginButton = factory.createLoginButton();
this.accountInput = factory.createAccountInput();
this.passwordInput = factory.createPasswordInput();
}
render(): void {
this.loginButton.render();
this.accountInput.render();
this.passwordInput.render();
}
}
const iosLoginPage = new LoginPage(new IOSLoginUIFactory());
iosLoginPage.render();
const androidLoginPage = new LoginPage(new AndroidLoginUIFactory());
androidLoginPage.render();
装饰者模式
动态地向对象添加新功能。
// 装饰者模式
abstract class Coffee {
abstract cost(): number;
abstract getDescription(): string;
}
abstract class Condiment extends Coffee {
coffee: Coffee;
constructor(coffee: Coffee) {
super();
this.coffee = coffee;
}
}
class Espresso extends Coffee {
getDescription(): string {
return 'americano';
}
cost(): number {
return 10;
}
}
class Ice extends Condiment {
cost(): number {
return 0.5 + this.coffee.cost();
}
getDescription(): string {
return `${this.coffee.getDescription()}, Ice`;
}
}
class Milk extends Condiment {
cost(): number {
return 1.5 + this.coffee.cost();
}
getDescription(): string {
return `${this.coffee.getDescription()}, Milk`;
}
}
class MilkFoam extends Condiment {
cost(): number {
return 3 + this.coffee.cost();
}
getDescription(): string {
return `${this.coffee.getDescription()}, MilkFoam`;
}
}
const espresso = new Espresso();
console.log(espresso.cost());
console.log(`${espresso.getDescription()}: ${espresso.cost()}¥`);
const cappuccino = new MilkFoam(new Milk(new Ice(new Espresso())));
console.log(`${cappuccino.getDescription()}: ${cappuccino.cost()}¥`);