Skip to main content

JavaScript 设计模式

设计模式

主要有3种类型的设计模式

  • 创建型设计模式:关注对象的创建机制
  • 结构型设计模式:关注对象之间关系的方法
  • 行为型设计模式:关注对象之间的通信

(创建型)生成器模式

创建复杂对象的构建过程,允许逐步构建对象。

构造函数就是该模式的一种实现方式。

class Person {
constructor(name, age, mother) {
this.name = name;
this.age = age;
this.mother = mother;
}
}

const tim = new Person('Tim', 31, null);
const tina = new Person('Tina', 57, null);

tim.mother = tina;

(创建型)工厂模式

将对象的创建过程封装在工厂类中。

function animalFactory() {
this.createAnimal = function(animalType) {
let animal;

switch(animalType) {
case 'dog':
animal = new Dog();
break;
case 'cat':
animal = new Cat();
break;
default:
break;
}

return animal;
}
}

const Dog = function() {
this.makeSound = () => {
console.log('woof woof!');
}
}

const Cat = function() {
this.makeSound = () => {
console.log('prrr prrr meow!');
}
}

const factory = new animalFactory();

const jojo = factory.createAnimal('dog');
jojo.makeSound();

const smokey = factory.createAnimal('cat');
smokey.makeSound();

(创建型)原型模式

基于现有对象来创建新对象的模式。

const macBook = {
color: 'silver',
turnOn() {
console.log('turning on...');
},
turnOff() {
console.log('turning off...');
}
}

// Proper prototype cloning
const myComputer = Object.create(macBook, { owner: { value: 'Tim'} });
console.log(myComputer.__proto__ === macBook);

macBook.power = 'USB-C';

// The protoype gets the new value for 'power'
console.log(myComputer.power);

(创建型)单例模式

确保一个类只有一个实例,并提供对该类的全局访问点。

const Database = (function () {
let instance;

function createDatabaseInstance() {
return new Object('Database Instance');
}

function getDatabaseInstance() {
if (!instance) {
instance = createDatabaseInstance();
}

return instance;
}

return { getDatabaseInstance }
})();

const databaseInstance1 = Database.getDatabaseInstance();
const databaseInstance2 = Database.getDatabaseInstance();

console.log(databaseInstance1 === databaseInstance2);

(结构型)包装模式

在不改变对象本身的情况下,为对象添加额外的行为或功能。

(结构型)组合模式

将对象组合成树结构以表示部分和整体的关系。

const Node = function(name) {
this.children = [];
this.name = name;
};

Node.prototype = {
add: function(child) {
this.children.push(child);
return this;
}
};

const init = () => {
const tree = new Node('root');
const [left, right] = [new Node("left"), new Node("right")];
const [leftleft, leftright] = [new Node("leftleft"), new Node("leftright")];
const [rightleft, rightright] = [new Node("rightleft"), new Node("rightright")];

tree.add(left).add(right);
left.add(leftleft).add(leftright);
right.add(rightleft).add(rightright);

console.log(tree);
};

init();

(结构型)模块模式

将代码封装在模块中,提供导出接口。

const userApi = () => {
const users = [];

const addUser = (name) => {
users.push(name);

return users[users.length -1];
}

const getAllUsers = () => {
return users;
}

const deleteUser = (name) => {
const userIndex = users.indexOf(name);

if (userIndex < 0) {
throw new Error('User not found');
}

users.splice(userIndex, 1);
}

const updateUser = (name, newName) => {
const userIndex = users.indexOf(name);

if (userIndex < 0) {
throw new Error('User not found');
}

users[userIndex] = newName;

return users[userIndex];
}

return {
add: addUser,
get: getAllUsers,
del: deleteUser,
put: updateUser
}
};

const api = userApi();

api.add('Tim');
api.add('Hina');
api.add('Yasmeen');
api.add('Neeloo');

console.log(api.get());

api.del('Yasmeen');

console.log(api.get());

api.put('Tim', 'Tim Winfred');

console.log(api.get());

(结构型)装饰器模式

动态地为对象添加新功能,而不改变其接口。

const Animal = function(type) {
this.type = type || 'dog';
}

const dog = new Animal('dog');
const cat = new Animal('cat');

dog.bark = function() {
console.log('woof woof!');
return this;
}

cat.meow = function() {
console.log('meow meooooooow!');
return this;
}

dog.bark();
cat.meow();

(结构型)外观模式

为复杂系统提供一个简化的接口,隐藏其复杂性。

import axios from 'axios';

const facade = {
get: function(url, params) {
return axios({
url,
params,
method: 'GET'
}).then(res => res.data);
}
};

function getUsers() {
return facade.get('https://jsonplaceholder.typicode.com/users');
}

function getUserById(id) {
return facade.get('https://jsonplaceholder.typicode.com/users', { id });
}

(结构型)代理模式

控制对其它对象的访问,允许在访问时添加额外的逻辑。

function CryptoCurrencyAPI() {
this.getValue = function(coin) {
console.log(`Calling Crypto API to get ${coin} price...`);

switch(coin.toLowerCase()) {
case 'bitcoin':
return 38000;
case 'ethereum':
return 2775;
case 'dogecoin':
return 0.39;
}
}
}

function CryptoCurrencyAPIProxy() {
this.api = new CryptoCurrencyAPI();
this.cache = {};

this.getValue = function(coin) {
if(!this.cache[coin]) {
console.log(`The value of ${coin} isn't stored in cache...`);
this.cache[coin] = this.api.getValue(coin);
}

return this.cache[coin];
}
}

const proxyAPI = new CryptoCurrencyAPIProxy();

console.log(proxyAPI.getValue('Bitcoin'));
console.log(proxyAPI.getValue('Bitcoin'));
console.log(proxyAPI.getValue('Ethereum'));
console.log(proxyAPI.getValue('Ethereum'));
console.log(proxyAPI.getValue('Dogecoin'));
console.log(proxyAPI.getValue('Dogecoin'));

(行为型)责任链模式

将请求的发送者和接收者解耦,允许多个对象处理请求。

const ATM = function() {
this.withdrawl = function(amount) {
console.log(`Requesting to withdrawl $${amount.toFixed(2)}`);

if (amount % 1 !== 0) {
console.log('Sorry, this ATM can\'t dispense coins. Please request another amount.');
return;
}

const dispenseOutput = {};

// chain or responsibility function
function get(bill) {
dispenseOutput[bill] = Math.floor(amount / bill);
amount -= dispenseOutput[bill] * bill;

return { get };
}

get(100).get(50).get(20).get(10).get(5).get(1);

this.dispense(dispenseOutput);
};

this.dispense = function(bills) {
console.log('--- Dispensing cash ---')
Object.entries(bills).forEach(([key, value]) => {
console.log(`- Dispensing ${value} $${key} bills`);
});
}
};

const myATM = new ATM();

myATM.withdrawl(126.10);
myATM.withdrawl(1287);
myATM.withdrawl(879);

(行为型)命令模式

将请求封装成对象,允许使用不同请求的客户端进行参数化操作。

const calculationMethods = {
add: function(x, y) {
return x + y;
},
subtract: function(x, y) {
return x - y;
},
multiply: function(x, y) {
return x * y;
},
divide: function(x, y) {
return x / y;
}
};

const calculator = {
execute: function(method, num1, num2) {
if (!(method in calculationMethods)) return null;

return calculationMethods[method](num1, num2);
}
};

console.log(calculator.execute('add', 1, 2));
console.log(calculator.execute('subtract', 5, 2));
console.log(calculator.execute('multiply', 11, 2));
console.log(calculator.execute('divide', 10, 2));
console.log(calculator.execute('square root', 20));

(行为型)观察者模式

定义对象之间的一对多依赖关系,其中一个对象的状态更改会导致其所有依赖项被通知和更新。

发布订阅就是该模式的一种实现方式。

function Subject() {
this.observers = [];
}

Subject.prototype = {
subscribe: function(observer) {
this.observers.push(observer);

return this;
},

unsubscribe: function(observer) {
const indexOfObserver = this.observers.indexOf(observer);
if (indexOfObserver > -1) {
this.observers.splice(indexOfObserver, 1);
}

return this;
},

notifyObserver: function(observer) {
const indexOfObserver = this.observers.indexOf(observer);
if (indexOfObserver > -1) {
this.observers[indexOfObserver].notify();
}

return this;
},

notifyAllObservers: function() {
this.observers.forEach(observer => {
observer.notify();
});

return this;
}
}

function Observer(name) {
this.name = name;
}

Observer.prototype = {
notify: function() {
console.log(`Observer ${this.name} has been notified`);
}
};

const subject = new Subject();

const observer1 = new Observer('user001');
const observer2 = new Observer('user002');
const observer3 = new Observer('user003');
const observer4 = new Observer('user004');
const observer5 = new Observer('user005');

subject.subscribe(observer1).subscribe(observer2).subscribe(observer3).subscribe(observer4).subscribe(observer5);
subject.notifyObserver(observer4);
subject.unsubscribe(observer4);
subject.notifyAllObservers();

(行为型)模板方法模式

定义一个算法的骨架,将一些步骤的实现推迟到子类。

class HouseTemplate {
constructor(name, address) {
this.name = name;
this.address = address;
}

buildHouse() {
this.buildFoundation();
this.buildPillars();
this.buildWalls();
this.buildWindows();
console.log(`${ this.name } has been built successfully at ${ this.address }`);
}

buildFoundation() {
console.log('Building foundation...');
}

buildPillars() {
throw new Error('You have to build your own pillars');
}

buildWalls() {
throw new Error('You have to build your own walls');
}

buildWindows() {
console.log('Building windows');
}
}

class WoodenHouse extends HouseTemplate {
constructor(name, address) {
super(name, address);
}

buildPillars() {
console.log('Building pillars for a wooden house');
}

buildWalls() {
console.log('Building walls for a wooden house');
}
}

class BrickHouse extends HouseTemplate {
constructor(name, address) {
super(name, address);
}

buildPillars() {
console.log('Building pillars for a brick house');
}

buildWalls() {
console.log('Building walls for a brick house');
}
}

const woodenHouse = new WoodenHouse('Wooden house', '123 Maple Road');
const brickHouse = new BrickHouse('Brick house', '456 Stone Lane');

woodenHouse.buildHouse();
brickHouse.buildHouse();

(行为型)策略模式

定义一系列算法,将它们封装成独立的对象,使得它们可以互相替换,以改变对象的行为。

function Regal() {
this.getTicketPrice = function(quantity) {
return quantity * 11.99;
}
}

function AMC() {
this.getTicketPrice = function(quantity) {
return quantity * 10.99;
}
}

function Cinemark() {
this.getTicketPrice = function(quantity) {
return quantity * 9.99;
}
}

function TicketPrice() {
this.theaterChain;

this.setTheaterChain = function(chain) {
this.theaterChain = chain;
}

this.calculate = function(quantity) {
return this.theaterChain.getTicketPrice(quantity);
}
}

const regal = new Regal();
const amc = new AMC();
const cinemark = new Cinemark();

const ticketPrice = new TicketPrice();
ticketPrice.setTheaterChain(regal);
console.log(ticketPrice.calculate(2));

ticketPrice.setTheaterChain(amc);
console.log(ticketPrice.calculate(3));

ticketPrice.setTheaterChain(cinemark);
console.log(ticketPrice.calculate(4));

参考文章

https://dev.to/twinfred/design-patterns-in-javascript-1l2l