设计模式
设计模式是一种在特定情境下解决问题的标准化方法。他描述了一个问题及该问题的解决方案,并且提供了一种重用这一解决方案的方式。设计模式就是一种解决在某个特定情境下问题的指导思想。
设计模式的类别:
- 创建型模式:关注对象的创建方式。
- 结构型模式:关注于如何组合类或对象形成更大的结构。
- 行为型模式:关注于对象间的职责分配和通信。
1.创建型模式(对象)
1.1 单例模式
-
定义:确保一个类只有一个实例,并提供一个全局访问点。
-
应用场景:配置管理、日志记录、浏览器窗口对象等。
-
示例代码
const singleton = (function(){ let instance function createInstance(){ return { counter : 0, incrementCounter: function(){ this.counter++ } } } return { getInstance: function(){ if(!instance){ instance = createInstance() } return instance } } })() const instance = singleton.getInstance() instance.incrementCounter() instance.incrementCounter() instance.incrementCounter() console.log(instance.counter); // 3
1.2 工厂模式
-
定义:定义一个创建产品对象的接口,让子类决定实例化哪一个类。创建单一类型的对象。
-
应用场景:组件创建、模块加载等。
-
示例代码:
class ProductA{ constructor(color, size){ this.color = color this.size = size } printProduct(){ console.log(`这个产品的颜色是${this.color},大小是${this.size}`); } } class ProductB{ constructor(color, size){ this.color = color this.size = size } printProduct(){ console.log(`这个产品的颜色是${this.color},大小是${this.size}`); } } function createProduct(type){ switch(type){ case "A" : return new ProductA('pink','medium') case "B" : return new ProductB('black', 'big') default: throw new Error('Invalid product type') } } const product = createProduct('A') product.printProduct(); // 这个产品的颜色是pink,大小是mediu
1.3 抽象工厂模式
-
定义:提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。创建一系列相关的对象产品。
-
应用场景:多主题样式管理、UI组件库等。
-
示例代码
class AbatractFactory{ createButton(){} createInput(){} } class ModernButton{ constructor(color, size){ this.color = color this.size = size } printProduct(){ console.log(`这个产品的颜色是${this.color},大小是${this.size}`); } } class ModernInput{ constructor(color, size){ this.color = color this.size = size } printProduct(){ console.log(`这个产品的颜色是${this.color},大小是${this.size}`); } } class ModernFactory extends AbatractFactory{ createButton(){ return new ModernButton('blue','small') } createInput(){ return new ModernInput() } } const modelFactory = new ModernFactory() const createButton = modelFactory.createButton() createButton.printProduct(); // 这个产品的颜色是blue,大小是small
2.结构型模式:(组合类或对象)
2.1 适配器模式
-
定义:允许一个类接口与另一个不兼容的类接口协同工作。
-
应用场景:第三方库集成、跨框架组件复用等。
-
示例代码
class ThitdPartyComponent{ constructor(name){ this.name = name } specificRequest(){ return '第三方库' } } class Adapter extends ThitdPartyComponent{ constructor(name){ super(name) } request(){ return `Adapter: (translated) ${this.specificRequest()}` } } const adapter = new Adapter('继承name') console.log(adapter.name); console.log(adapter.request());
2.2 装饰器模式
-
定义:动态地给一个对象添加一些额外的职责。
-
应用场景:权限控制、日志记录等。
-
示例代码
/* 2.2 装饰器模式 定义:动态地给一个对象添加一些额外的职责。 应用场景:权限控制、日志记录等。 */ function Component(){ // console.log(this); this.operation = function(){ console.log('Concrete component'); } } function Decorator(component){ this.component = component } // Decorator的原型指向 Object.create(Component.prototype)这个对象,并且这个对象继承Component.prototype // 让 Decorator 的原型继承自 Component.prototype Decorator.prototype = Object.create(Component.prototype) // console.log(Decorator); // Decorator原型上添加 operation 方法 Decorator.prototype.changeOperation = function(){ // 调用继承来自Component 对象身上的operation方法 console.log(this); this.component.operation() console.log('Decorator'); } const component = new Component() const decorator = new Decorator(component) component.operation() // Concrete component // decorator.operation() // 报错 decorator.changeOperation()
2.3 组合模式
-
定义:将对象组合成树形结构以表示“部分-整体”的层次结构。
-
应用场景:文件系统、UI组件树等。
-
示例代码:
class Component { constructor(name) { this.name = name } add(component) { } remove(component) { } display(level) { } } class Leaf extends Component { display(level) { console.log(`Leaf的display`, level); console.log(`${" ".repeat(level * 2)} Leaf:${this.name}`); } } class Composite extends Component { constructor(name) { super(name) this.children = [] } add(component) { this.children.push(component) } remove(component) { const index = this.children.indexOf(component) if (index > -1) { this.children.splice(index, 1) } } display(level) { console.log("Composite的display", level); // " ".repeat(level * 2),缩进空格 console.log(`${" ".repeat(level * 2)} Composite: ${this.name}`); this.children.forEach(child => child.display(level + 1)) } } const root = new Composite('root') const branch1 = new Composite('branch1') // 生成一级分支: branch1 const branch2 = new Composite('branch2') // 生成二级分支: branch2 root.add(branch1) root.add(branch2) branch1.add(new Leaf('leaf1')) branch1.add(new Leaf('leaf2')) branch2.add(new Leaf('leaf3')) branch2.add(new Leaf('leaf4')) console.log(JSON.stringify(root)); root.display(0) // 生成的树结构为: // const rootNode = { // "name": "root", // "children": [ // { "name": "branch1", "children": [{ "name": "leaf1" }, { "name": "leaf2" }] }, // { "name": "branch2", "children": [{ "name": "leaf3" }, { "name": "leaf4" }] }] // }
3.行为型模式(对象间的职责分配和通信)
3.1 观察者模式
-
定义:定义对象间的一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
-
应用场景:事件处理、状态监测等。
-
示例代码:
class Subject{ constructor(){ this.observers = [] // 存储每一个订阅者observer,使用对象的依赖 } addObserver(observer){ this.observers.push(observer) } removeObserver(observer){ const index = this.observers.indexOf(observer) if(index > -1){ this.observers.splice(index, 1) } } notify(data){ this.observers.forEach(observer => observer.update(data)) } } class Observer{ update(data){ console.log(`Observer recived: ${data}`); } } const subject = new Subject() const observer1 = new Observer() const observer2 = new Observer() subject.addObserver(observer1) subject.addObserver(observer2) subject.notify('hello, observers!') // 打印两边: Observer recived: hello, observers!
3.2 策略模式
-
定义:定义一系列算法,把它们一个个封装起来,并且使它们可相互替换。
-
应用场景:支付方式选择、排序算法等。
-
示例代码:
class Strategy{ // 执行函数 execute(data){ throw new Error('Method not implemented') } } class ConcreteStrategyA extends Strategy{ execute(data){ return `Sorting with strategy A: ${data.sort().join(',')}` } } class ConcreteStrategyB extends Strategy{ execute(data){ return `Sorting with strategy B: ${data.reverse().join(',')}` } } class Context{ constructor(strategy){ this.strategy = strategy } setStrategy(strategy){ this.strategy = strategy } executeStratety(data){ return this.strategy.execute(data) } } // 实例化context,使用策略一 const context = new Context(new ConcreteStrategyA()) console.log(context.executeStratety([3,5,6,1,8])); // 使用另外一种策略 context.setStrategy(new ConcreteStrategyB()) console.log(context.executeStratety([3,5,6,1,8]));
3.3命令模式
-
定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户端进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
-
应用场景:UI按钮操作、命令行工具等。
-
示例代码
class Command{ constructor(receiver){ this.receiver = receiver } // 执行办法,子类继承说明具体的办法 execute(){} } // 具体的命令 class ConcreteCommand extends Command{ execute(){ this.receiver.action() } } // 接收方 class Receiver{ // 行为操作 action(){ console.log(`Receiver: Action executed`); } } // 用具体命令对象,触发命令执行的对象 class Invoker{ constructor(){ this.commands = [] } takeCommand(command){ this.commands.push(command) } placeCommands(){ this.commands.forEach(command => command.execute()) } } const invoker = new Invoker() const receiver1 = new Receiver() const receiver2 = new Receiver() const concreteCommand1 = new ConcreteCommand(receiver1) const concreteCommand2 = new ConcreteCommand(receiver2) invoker.takeCommand(concreteCommand1) invoker.takeCommand(concreteCommand2) invoker.placeCommands() // Receiver: Action executed
4.实战案例
4.1 案例1:单例模式(确保一个类只有一个实例,并提供一个全局访问点)
背景:实现一个全局的配置管理器。
实现:使用闭包和构造函数相结合的方式创建单例。
应用场景:前端项目中的配置管理、全局事件监听等。
// 闭包封装私有变量和构造函数
function createConfigManager(){
let instance = null
let configs = {}
// 构造函数
function configManager(){
if(instance){
return instance
}
instance = this // 实例为当前this指向
}
configManager.prototype = {
// 添加配置项
setConfig: function(key, value){
configs[key] = value
},
// 获取配置项
getConfig: function(key){
return configs[key]
},
// 删除配置项
removeConfig: function(key){
delete configs[key]
}
}
return function getInstance(){
return new configManager()
}
}
const getConfigManager = createConfigManager();
// 使用示例: 返回一个实例
const manager1 = getConfigManager();
manager1.setConfig('apiUrl', 'https://api.example.com');
console.log(manager1.getConfig('apiUrl')); // 输出: https://api.example.com
const manager2 = getConfigManager();
console.log(manager1 === manager2); // 输出: true,说明是单例
manager2.setConfig('theme', 'dark');
console.log(manager1.getConfig('theme')); // 输出: dark,因为它们是同一个实例
// 清理或删除配置项
manager1.removeConfig('apiUrl');
console.log(manager1.getConfig('apiUrl')); // 输出: undefined,因为该配置项已被删除
4.2 案例2:观察者模式:(定义对象间的一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。)
背景:实现一个简单的事件监听器。
实现:使用数组存储观察者,并在事件触发时遍历执行。
应用场景:DOM事件绑定、自定义事件发布订阅等。
// 创建观察者的类, 每一个观察者都有数据更新的回调函数
class Observer{
constructor(updateCallback){
this.update = updateCallback
}
}
// 一个类,实现当该对象内数据变化,会通知所有用到这个对象的订阅者,进行数据更新
class Subject{
constructor(){
this.data = null
this.obervers = []
}
// 订阅事件
subscribe(observer){
this.obervers.push(observer)
}
// 取消事件
cancel(oberver){
const index = this.obervers.indexOf(oberver)
if(index > -1){
this.obervers.splice(index, 1)
}
}
// 通知所有订阅者数据已变化
notify(newData){
this.data = newData
this.obervers.forEach(observer => {
observer.update(newData)
})
}
// 设置数据并通知所有订阅者
setData(newData){
// 通知每个订阅者
this.notify(newData)
}
}
// 实例化数据对象
const subject = new Subject()
const observer1 = new Observer((newData) => {
// 可以对新数据进行处理
console.log(`Observer 1 received new data: ${newData}`);
})
const observer2 = new Observer((newData) => {
// 可以对新数据进行处理
console.log(`observer 2 received new data: ${newData}`);
})
subject.subscribe(observer1)
subject.subscribe(observer2)
subject.setData('Initial data') // Observer 1 received new data: Initial data observer 2 received new data: Initial data
// 取消订阅者2
subject.cancel(observer2)
subject.setData('set newData') // Observer 1 received new data: set newData
观察者模式和发布订阅模式?
观察者模式
目标者和观察者对象相互依赖,观察者对某个对象的状态进行观察,当对象的状态发生变化的时候,通知所有依赖这个对象的观察者。目标提供维护观察者的提供一系列方法,观察者提供更新方法,观察者把自己注册到具体目标中去,当目标发生变化,调用观察者提供的方法进行更新。
举个例子:
比如有个天气中心的具体目标A,监听天气变化,同时有个显示天气界面的观察者B,B就会把自己注册到A中,当A中触发了天气变化事件,就会调用B中的更新方法,然后界面就会进行更新。
- 目标者对象【Suject】:拥有方法:【删除、添加、通知】Observer;
- subs数组:存储所有的观察者;
- addSub():添加观察者;
- notify():当目标对象状态发生变化后调用所有观察者的update()方法。
- 观察者对象【Observer】:拥有方法:接受Subject状态变更通知并进行对应的处理。
- update():当数据对象发生变化时,具体要做的事情。
// 目标者(发布者)
class Subject {
constructor () {
// 记录所有的订阅者
this.subs = []
}
// 添加订阅者
addSub (sub) {
if (sub && sub.update) {
this.subs.push(sub)
}
}
// 发布通知
notify () {
this.subs.forEach(sub => {
sub.update()
})
}
}
// 观察者(订阅者)
class Watcher {
update () {
console.log('update')
}
}
// 测试一下
let dep = new Subject()
let watcher = new Watcher()
let watcher1 = new Watcher()
// 添加订阅
dep.addSub(watcher)
dep.addSub(watcher1)
// 开启通知
dep.notify()
发布订阅模式
基于一个事件中心,接受通知的对象是订阅者,需要先订阅某个事件,触发事件的对象是发布者,发布者通过触发事件,通知各个订阅者。发布者将消息发布到特定的消息队列或主题中,而订阅者可以订阅这些消息队列或主题以接收和处理消息。发布者和订阅者之间的通信是异步的,这意味着发布者发布消息后,订阅者可以在任何时候接收和处理消息。
举个例子:
订阅公众号。公众号(事件中心),订阅公众号的大家(订阅者),维护这个公众号的作者(发布者),当发布者发布新的文章后,订阅者能够通过公众号平台接收到消息。
class PubSub {
constructor() {
this.subscribers = {}
}
subscribe(type, fn) {
if (!Object.prototype.hasOwnProperty.call(this.subscribers, type)) {
this.subscribers[type] = [];
}
this.subscribers[type].push(fn);
}
unsubscribe(type, fn) {
let listeners = this.subscribers[type];
if (!listeners || !listeners.length) return;
this.subscribers[type] = listeners.filter(v => v !== fn);
}
publish(type, ...args) {
let listeners = this.subscribers[type];
if (!listeners || !listeners.length) return;
listeners.forEach(fn => fn(...args));
}
}
let ob = new PubSub();
ob.subscribe('add', (val) => console.log(val));
ob.publish('add', '哈哈哈哈 触发了add事件');
ob.subscribe('logInfo', (msg) => console.log(msg));
ob.publish('logInfo', '消息很重要!');
观察者模式和发布订阅模式的区别
相同点:
- 一对多的依赖关系:在这两种模式中,多个观察者/订阅者都依赖于一个单一的主体(发布者或被观察对象)。
- 解耦:这两种模式都提供了一种将发送者和接收者解耦的方式。发送者不需要知道接收者的具体信息,接收者只需知道如何响应接收到的消息或通知。
- 动态更新:都允许当主体变化时动态地通知所有依赖者,使它们能自动更新自己的状态或处理信息。
不同点:
- 实现机制:
- 观察者模式处理的消息,消息数据的生产方和消费方都要和消息对象本身紧密关联。观察者模式是基于数据对象本身的;
- 发布订阅模式跟消息对象本身没有紧密的关系,而是通过一个调度中心传递消息。发布订阅模式是基于主题或者消息队列的。
- 耦合度:
- 观察者模式中,被观察对象通常持有观察者的引用,因此耦合度相对较高。
- 发布/订阅模式通过使用消息队列或事件总线,减少了发布者和订阅者之间的直接依赖,因而耦合度较低。
- 应用场景:
- 观察者模式多用于实现单一应用程序中的同步更新;例如,GUI应用程序中视图与数据模型的更新。
- 发布/订阅模式适用于异步消息传递场景,如跨应用程序或跨服务的消息通信,尤其是在分布式系统中。
- 异步处理:
- 在观察者模式中,被观察者对观察者的更新通常是同步进行的,也就是说,当一个观察者被通知时,必须等待这个观察者处理完成后才能继续进行下去。
- 发布/订阅模式中,发布者发布消息后不需要等待订阅者处理,订阅者可以异步接收并处理消息,这有利于提高系统的响应能力和吞吐量。