来源 | https://www.cnblogs.com/yinpengfei/p/17397951.html
设计模式简介
根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。
本文主要简述了其中一些常用的设计模式,希望对你有用。
在代码封装性、可读性、重用性、可扩展性、可靠性等方面,使项目更易于开发、维护及扩展。
在工厂模式中,我们在创建对象时不会对外部暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
class ProductA {
constructor(name) {
this.name = name
}
produce() {
console.log("produce A is producing..")
return `produce A: ${this.name}`
}
}
class ProductB {
constructor(name) {
this.name = name
}
produce() {
console.log("produce B is producing..")
return `product B: ${this.name}`
}
}
class Factory {
create(type, name) {
switch (type) {
case "A":
return new ProductA(name)
case "B":
return new ProductB(name)
default:
throw new Error("不存在的产品类型")
}
}
}
// 使用
const factory = new Factory()
const productA = factory.create("A", "productA")
const productB = factory.create("B", "productB")
productA.produce() // produce A is producing..
productB.produce() // produce B is producing..
class Singleton {
constructor() {
if (typeof Singleton.instance === "object") {
return Singleton.instance
}
this.name = "Singleton"
Singleton.instance = this
return this
}
}
const singleton1 = new Singleton()
const singleton2 = new Singleton()
console.log("对比:", singleton1 === singleton2) // true
外观模式隐藏系统的复杂性,并向外部提供了一个可以访问系统的接口。它向现有的系统添加一个接口,来隐藏系统的复杂性。
(1) 应用外观模式封装一个统一的 DOM 元素事件绑定/取消方法,用于兼容不同版本的浏览器和更方便的调用
// 绑定事件
function addEvent(element, event, handler) {
if (element.addEventListener) {
element.addEventListener(event, handler, false)
} else if (element.attachEvent) {
element.attachEvent("on" + event, handler)
} else {
element["on" + event] = fn
}
}
// 取消绑定
function removeEvent(element, event, handler) {
if (element.removeEventListener) {
element.removeEventListener(event, handler, false)
} else if (element.detachEvent) {
element.detachEvent("on" + event, handler)
} else {
element["on" + event] = null
}
}
(2) 组织方法模块细化多个接口,并由外观类去进行执行调用
function model1 () {
// do something...
}
function model2 () {
// do something...
}
function use () {
model1()
model2()
}
在代理模式中,一个类代表另一个类的功能。
class Image {
constructor(url) {
this.url = url
this.loadImage()
}
loadImage() {
console.log(`Loading image from ${this.url}`)
}
}
class ProxyImage {
constructor(url) {
this.url = url
}
loadImage() {
if (!this.image) {
this.image = new Image(this.url)
}
console.log(`Displaying cached image from ${this.url}`)
}
}
const image1 = new Image('https://example.com/image1.jpg')
const proxyImage1 = new ProxyImage('https://example.com/image1.jpg')
proxyImage1.loadImage(); // Loading image from https://example.com/image1.jpg
proxyImage1.loadImage(); // Displaying cached image from https://example.com/image1.jpg
在策略模式中,一个类的行为或其算法可以在运行时更改。我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
function Strategy (type,a,b) {
const Strategyer = {
add: function (a, b) {
return a + b
},
subtract: function (a, b) {
return a - b
},
multip: function (a, b) {
return a / b
},
}
return Strategyer[type](a, b)
}
迭代器模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
迭代器模式解决了此些问题:
一个迭代器通常需要实现以下接口:
(1) 为 js 数组实现一个迭代器
const item = [1, 2, 3, 4, 5]
function Iterator(items) {
this.items = items;
this.index = 0;
}
Iterator.prototype = {
hasNext: function () {
return this.index < this.items.length;
},
next: function () {
return this.items[this.index++];
}
}
// use
const iterator = new Iterator(item);
while(iterator.hasNext()){
console.log('迭代器:',iterator.next()); // 1, 2, 3, 4, 5
}
(2)实现一个 Range 类用于在某个数字区间进行迭代
ES6 提供了更简单的迭代循环语法 for...of,使用该语法的前提是操作对象需要实现 可迭代协议(The iterable protocol),简单说就是该对象有个 Key 为 Symbol.iterator 的方法,该方法返回一个 iterator 对象。
function Range(start, end) {
return {
[Symbol.iterator]: function () {
return {
next() {
if (start < end) {
return { value: start++, done: false }
}
return { value: end, done: true }
}
}
}
}
}
// use
for (const el of Range(1, 5)) {
console.log('el:', el) // 1, 2, 3, 4
}
观察者模式主要是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
被观察对象(subject)维护一组观察者(observer),当被观察对象状态改变时,通过调用观察者的某个方法将这些变化通知到观察者。
比如给 DOM 元素绑定事件的 addEventListener() 方法:
dom.addEventListener(type, listener [, options])
dom 就是被观察对象 Subject,listener 就是观察者 Observer。
观察者模式中 Subject 对象一般需要实现以下 API:
// 被观察者
function Subject() {
this.observers = []
}
Subject.prototype = {
// 订阅
subscribe: function (observer) {
this.observers.push(observer)
},
// 取消订阅
unsubscribe: function (observerToRemove) {
this.observers = this.observers.filter(observer => {
return observer !== observerToRemove
})
},
// 事件触发
fire: function () {
this.observers.forEach(observer => {
observer.call()
})
}
}
// use
const subject = new Subject()
const observer1 = () => {
console.log('observer1 触发了...')
}
const observer2 = () => {
console.log('observer2 触发了...')
}
subject.subscribe(observer1)
subject.subscribe(observer2)
subject.fire() // observer1 触发了... observer2 触发了...
用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。
中介者模式和观察者模式有一定的相似性,都是一对多的关系,也都是集中式通信,不同的是中介者模式是处理同级对象之间的交互,而观察者模式是处理 Observer 和 Subject 之间的交互。
// 聊天室成员类
class Member {
constructor(name) {
this.name = name
this.chatroom = null
}
// 发送消息
send (message, toMember) {
this.chatroom.send(message, this, toMember)
}
// 接收消息
receive (message, fromMember) {
console.log(`${fromMember.name} to ${this.name}: ${message}`)
}
}
// 聊天室类
class Chatroom {
constructor() {
this.members = {}
}
// 增加成员
addMember (member) {
this.members[member.name] = member
member.chatroom = this
}
// 发送消息
send (message, fromMember, toMember) {
toMember.receive(message, fromMember)
}
}
// use
const chatroom = new Chatroom()
const John = new Member('John')
const Tom = new Member('Tom')
chatroom.addMember(John)
chatroom.addMember(Tom)
John.send('Hi Tom!', Tom) // John to Tom: Hi Tom!
使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。
// 财务报表类
class Report {
constructor(income, cost, profit) {
this.income = income
this.cost = cost
this.profit = profit
}
}
// 老板类
class Boss {
get (data) {
console.log(`老板访问报表数据,盈利:${data}`)
}
}
// 财务人员类
class Account {
get (num1, num2) {
console.log(`财务人员访问报表数据,收入:${num1},支出: ${num2}`)
}
}
// 访问者类
function vistor(data, person) {
const handle = {
Boss: function (data) {
person.get(data.profit)
},
Account: function (data) {
person.get(data.income, data.cost)
}
}
handle[person.constructor.name](data)
}
// use
const report = new Report(1000, 500, 200);
vistor(report, new Account()) // 财务人员访问报表数据,收入:1000,支出: 500
vistor(report, new Boss()) // 老板访问报表数据,盈利:200
以上是一些常见的设计模式,但仅冰山一角,还有很多的设计模式可应用于不同的场景。了解或熟悉这些设计模式或许可以潜移默化地提升我们的开发水平和效率。