总结一些前端项目中常见的设计模式
# 一、工厂模式
工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象,用工厂方法代替new操作的一种模式。
特征:
1、构造函数和创建者分离,对new操作进行封装
2、符合开放封闭原则
// 举个栗子
// 食品实例生成的详细过程
class Food {
constructor(name, type) {
this.name = name // 食品
this.type = type // 食品类型
}
desc() {
console.log(`我是${this.name},属于${this.type}类型`);
}
}
class FoodFactory {
// ES6中静态方法不会被实例调用,可以被类调用
static create(name, type) {
// 创建一种食品实例
return new Food(name, type)
}
}
// 工厂开始生产食品实例了
FoodFactory.create('核桃', '坚果').desc() // 我是核桃, 属于坚果类型
FoodFactory.create('旺仔', '饮品').desc() // 我是旺仔, 属于饮品类型
// 这样我们就可以 生成出我们想要的食品, 只要你给我们 一个名称和类型
# 二、单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
特征: 1、单例模式的主要思想就是,实例如果已经创建,则直接返回
class Singleton {
// js模拟
constructor() {}
login() {
console.log('login...');
}
}
// 给类挂载一个静态方法, 只有一个
// 自执行函数: 只是为了将变量保存在 函数的作用域中, 避免污染而已
Singleton.singleInstance = (function () {
// 通过闭包实现: 类似通过一个全局变量来存储这个实例
let instance; // 保存创建好的实例
return function () {
if (!instance) {
// 如果没有创建, 就创建一个
instance = new Singleton()
}
return instance
}
})()
// 通过调用静态方法来创建单实例
let single11 = Singleton.singleInstance()
let single22 = Singleton.singleInstance()
// 通过调用类 初始化的实例
let single33 = new Singleton()
console.log(single11 === single22); // ture
console.log(single11 === single33); // false
# 三、观察者模式/发布订阅模式
# 1、观察者模式
观察者模式 其实就是一个目标对象(Subject),维护一系列依赖与他的对象(Obsever),当目标对象(Subject)状态发生改变自动通知它们(Oberver)。
模式逻辑入下图:
代码实现:
// 目标对象
class Subject{
constructor(){
// 观察者列表
this.observers = []
}
// 添加观察者
addObserver(observer){
this.observers.push(observer)
}
// 删除观察者
removeObserver(observer){
let index = this.observers.findIndex(o => o === observer)
if(o != -1){
this.observers.splice(index,1)
return true
}
return false
}
// 通知
notify(msg){
this.observers.forEach(o=>o.update())
}
}
// 观察者
class Observer{
constructor(name){
this.name = name
}
// 自定义的更新方法
update(){
console.log(this.name + '执行更新操作!')
}
}
// 创建目标对象、观察者
let sub = new Subject()
let ob1 = new Observer('观察者1')
let ob2 = new Observer('观察者2')
// 订阅 观察者
sub.addObserver(ob1)
sub.addObserver(ob2)
// 目标发布通知
sub.notify()
// 删除 观察者订阅
sub.removeObserver(ob1)
// 再次发布通知
sub.notify()
# 2、发布订阅模式
发布-订阅模式,就是一个发布者对象(Publisher)通过一个事件通道(Event Channel)发布指定的事件,通知多个订阅该事件的订阅者对象(Subscriber)。
模式逻辑入下图:
代码实现:
// 发布-订阅类
class PublishSubscribe{
constructor(){
// 事件列表
this._events = {}
}
// 发布事件
$emit(topic, arg){
if(this._events[topic]){
this._events[topic].forEach(fn => fn(arg))
}
}
// 订阅
$on(topic, fn){
if(this._events[topic]){
this._events[topic].push(fn)
}else{
this._events[topic] = [fn]
}
}
// 取消订阅
$off(topic,fn){
if(this._events[topic]){
if(fn){
let index = this._events[topic].findIndex(e => e === fn)
if(index != -1){
this._events[topic].splice(index,1)
}
}else{
delete this._events[topic]
}
}
}
}
// 创建发布-订阅实例
let pubSub = new PublishSubscribe()
// 订阅 'log' 事件
pubSub.$on('log',(msg)=>{
console.log('log1',msg)
})
pubSub.$on('log',(msg)=>{
console.log('log2',msg)
})
function consoleLog(msg){
console.log('log3',msg)
}
pubSub.$on('log',consoleLog)
// 订阅 error 事件
pubSub.$on('error',(msg)=>{
console.log('error',msg)
})
// 发布事件
pubSub.$emit('log','输出日志')
pubSub.$emit('error','报错啦')
// 控制台打印:
// log1 输出日志
// log2 输出日志
// log3 输出日志
// error 报错啦
// 取消单个订阅
pubSub.$off('log', consoleLog)
// 再次发布事件
pubSub.$emit('log','再次输出日志')
// 控制台打印:
// log1 输出日志
// log2 输出日志
// 取消指定类型的所有订阅
pubSub.$off('log')
// 再次发布事件
pubSub.$emit('log','输出日志') // 无打印内容
# 3、两种模式之间的差异
- Observer模式,观察者直接订阅内容改变的事件,定义一个对多的依赖关系
- Publish/Subscribe模式,通过一个事件通道关联发布者和订阅者,发布者与订阅者不存在直接依赖关系。