Skip to main content

设计模式

单例模式

一个类只有一个实例

发布-订阅模式

// Node中的EventEmitter 就是用的发布订阅模式
class EventEmitter {
constructor() {
this.list = {}
}

on(name, fn, type = 1) {
if (!this.list[name]) {
this.list[name] = []
}
this.list[name].push([fn, type])

}

once(name, fn, type = 0) {
this.on(name, fn, type)
}

emit(name, ...args) {
let fns = this.list[name]
if (!fns || fns.length === 0) return
fns.forEach((fn, index) => {
fn[0].apply(this, args)
if (fn[1] === 0) {
fns.splice(index, 1)
}
})
}

remove(name, func) {
let fns = this.list[name]
if (!fns) {
this.list[name] = []
}
fns.forEach((fn, index) => {
if (fn[0] === func) {
fns.splice(index, 1)
}
})
}
}

let bus = new EventEmitter()

bus.on("click", (value) => {
console.log(value)
})

bus.emit("click", 111)

观察者模式

class Publisher {
constructor() {
this.list = []
}

addListener(listener) {
this.list.push(listener)
}

removeListener(listener) {
this.list.forEach((item, index) => {
if (listener === item) {
this.list.splice(index, 1)
}
})
}

notify(obj) {
this.list.forEach((item) => {
item.process(obj)
})
}
}

class Subscriber {
process(obj) {
console.log(obj.name)
}
}

MVC

MVC (Model-View-Controller) 分为三部分

  • Model(数据模型):数据
  • View(视图):用户界面
  • Controller(控制器):业务逻辑

通信过程如下,所有通信都是单向的。

  1. View 传送指令到 Controller
  2. Controller 完成业务逻辑后,要求 Model 改变状态
  3. Model 将新的数据发送到 View,用户得到反馈

过程

MVVM

MVVM(Model-View-ViewModel)也分为三部分,数据模型,视图,视图模型。

与MVC的区别之一在于View和Model之间要借助ViewModel进行通信。

通信过程

工厂模式

工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。

// 简单工厂模式
class CarFactory {
static createCar(type) {
switch (type) {
case 'BMW':
return new BMW();
case 'Audi':
return new Audi();
case 'Mercedes':
return new Mercedes();
default:
throw new Error('Unknown car type');
}
}
}

class BMW {
constructor() {
this.brand = 'BMW';
this.type = 'Luxury';
}
}

class Audi {
constructor() {
this.brand = 'Audi';
this.type = 'Luxury';
}
}

// 使用
const bmw = CarFactory.createCar('BMW');
const audi = CarFactory.createCar('Audi');

装饰器模式

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。

class Coffee {
cost() {
return 5;
}

description() {
return 'Simple coffee';
}
}

class MilkDecorator {
constructor(coffee) {
this.coffee = coffee;
}

cost() {
return this.coffee.cost() + 2;
}

description() {
return this.coffee.description() + ', milk';
}
}

class SugarDecorator {
constructor(coffee) {
this.coffee = coffee;
}

cost() {
return this.coffee.cost() + 1;
}

description() {
return this.coffee.description() + ', sugar';
}
}

// 使用
let coffee = new Coffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
console.log(coffee.description()); // Simple coffee, milk, sugar
console.log(coffee.cost()); // 8

策略模式

策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。

// 策略类
class DiscountStrategy {
calculate(amount) {
throw new Error('calculate method must be implemented');
}
}

class RegularCustomer extends DiscountStrategy {
calculate(amount) {
return amount;
}
}

class PremiumCustomer extends DiscountStrategy {
calculate(amount) {
return amount * 0.9; // 10% 折扣
}
}

class VIPCustomer extends DiscountStrategy {
calculate(amount) {
return amount * 0.8; // 20% 折扣
}
}

// 上下文类
class ShoppingCart {
constructor(discountStrategy) {
this.discountStrategy = discountStrategy;
this.amount = 0;
}

setDiscountStrategy(discountStrategy) {
this.discountStrategy = discountStrategy;
}

addItem(price) {
this.amount += price;
}

calculateTotal() {
return this.discountStrategy.calculate(this.amount);
}
}

// 使用
const cart = new ShoppingCart(new RegularCustomer());
cart.addItem(100);
cart.addItem(50);
console.log(cart.calculateTotal()); // 150

cart.setDiscountStrategy(new VIPCustomer());
console.log(cart.calculateTotal()); // 120

适配器模式

适配器模式允许接口不兼容的类可以一起工作。

// 旧的接口
class OldPrinter {
oldPrint(text) {
console.log(`Old printer: ${text}`);
}
}

// 新的接口
class NewPrinter {
print(text) {
console.log(`New printer: ${text}`);
}
}

// 适配器
class PrinterAdapter {
constructor(oldPrinter) {
this.oldPrinter = oldPrinter;
}

print(text) {
this.oldPrinter.oldPrint(text);
}
}

// 使用
const oldPrinter = new OldPrinter();
const adapter = new PrinterAdapter(oldPrinter);
adapter.print('Hello World'); // Old printer: Hello World

代理模式

代理模式为其他对象提供一种代理以控制对这个对象的访问。

class RealImage {
constructor(filename) {
this.filename = filename;
this.loadFromDisk();
}

loadFromDisk() {
console.log(`Loading ${this.filename} from disk...`);
}

display() {
console.log(`Displaying ${this.filename}`);
}
}

class ProxyImage {
constructor(filename) {
this.filename = filename;
this.realImage = null;
}

display() {
if (!this.realImage) {
this.realImage = new RealImage(this.filename);
}
this.realImage.display();
}
}

// 使用
const image = new ProxyImage('photo.jpg');
// 图片只有在需要显示时才会被加载
image.display(); // Loading photo.jpg from disk... Displaying photo.jpg
image.display(); // Displaying photo.jpg (不会重新加载)

命令模式

命令模式将请求封装成对象,从而使您可以用不同的请求对客户进行参数化。

// 接收者
class Light {
turnOn() {
console.log('Light is ON');
}

turnOff() {
console.log('Light is OFF');
}
}

// 命令接口
class Command {
execute() {
throw new Error('execute method must be implemented');
}

undo() {
throw new Error('undo method must be implemented');
}
}

// 具体命令
class TurnOnCommand extends Command {
constructor(light) {
super();
this.light = light;
}

execute() {
this.light.turnOn();
}

undo() {
this.light.turnOff();
}
}

class TurnOffCommand extends Command {
constructor(light) {
super();
this.light = light;
}

execute() {
this.light.turnOff();
}

undo() {
this.light.turnOn();
}
}

// 调用者
class RemoteControl {
constructor() {
this.command = null;
this.history = [];
}

setCommand(command) {
this.command = command;
}

pressButton() {
if (this.command) {
this.command.execute();
this.history.push(this.command);
}
}

pressUndo() {
if (this.history.length > 0) {
const lastCommand = this.history.pop();
lastCommand.undo();
}
}
}

// 使用
const light = new Light();
const turnOnCommand = new TurnOnCommand(light);
const turnOffCommand = new TurnOffCommand(light);
const remote = new RemoteControl();

remote.setCommand(turnOnCommand);
remote.pressButton(); // Light is ON
remote.pressUndo(); // Light is OFF

remote.setCommand(turnOffCommand);
remote.pressButton(); // Light is OFF
remote.pressUndo(); // Light is ON