欢迎光临
我们一直在努力

设计 模式

文章目录

  • 前言
  • 一、单例模式
    • 1.1 定义
    • 1.2 应用场景
  • 二、观察者模式 vs 发布订阅模式
    • 2.1 观察者模式
    • 2.2 发布订阅模式
    • 2.3 区别
  • 三、策略模式
    • 3.1 定义
    • 3.2 表单验证
  • 四、代理模式 vs 装饰器模式
    • 4.1 代理模式
    • 4.2 装饰器模式
    • 4.3 区别
  • 五、工厂模式
    • 5.1 简单工厂
    • 5.2 工厂方法
  • 六、与 Redux、EventBus 的映射
    • 6.1 Redux 中的设计模式
    • 6.2 EventBus 中的设计模式
  • 七、易混淆点
  • 八、思考与练习
  • 总结

前言

设计模式是解决特定问题的成熟方案,在前端开发中广泛应用。本篇会讲清楚:

  • 单例模式
  • 观察者模式 vs 发布订阅模式
  • 策略模式
  • 代理模式 vs 装饰器模式
  • 工厂模式
  • 与 Redux、EventBus 等的映射关系

一、单例模式

1.1 定义

保证一个类只有一个实例,并提供全局访问点。

class Singleton {
static instance = null

static getInstance() {
if (!Singleton.instance) {
Singleton.instance = new Singleton()
}
return Singleton.instance
}
}

const a = Singleton.getInstance()
const b = Singleton.getInstance()
console.log(a === b) // true

1.2 应用场景

// 1. 全局状态管理(Redux Store)
const store = createStore(reducer) // 全局唯一

// 2. 数据库连接池
class DatabasePool {
static instance = null
static getInstance() {
if (!DatabasePool.instance) {
DatabasePool.instance = new DatabasePool()
}
return DatabasePool.instance
}
}

// 3. 弹窗管理器
class ModalManager {
static instance = null
static getInstance() {
if (!ModalManager.instance) {
ModalManager.instance = new ModalManager()
}
return ModalManager.instance
}
}


二、观察者模式 vs 发布订阅模式

2.1 观察者模式

对象直接订阅目标,目标变化时通知所有观察者。

class Subject {
observers = []

subscribe(observer) {
this.observers.push(observer)
}

unsubscribe(observer) {
this.observers = this.observers.filter(obs => obs !== observer)
}

notify(data) {
this.observers.forEach(observer => observer.update(data))
}
}

class Observer {
update(data) {
console.log('收到通知:', data)
}
}

// 使用
const subject = new Subject()
const observer1 = new Observer()
const observer2 = new Observer()

subject.subscribe(observer1)
subject.subscribe(observer2)
subject.notify('数据更新了')

2.2 发布订阅模式

发布者和订阅者不直接通信,通过事件中心调度。

class EventEmitter {
events = {}

on(event, callback) {
if (!this.events[event]) {
this.events[event] = []
}
this.events[event].push(callback)
}

off(event, callback) {
if (!this.events[event]) return
this.events[event] = this.events[event].filter(cb => cb !== callback)
}

emit(event, …args) {
if (!this.events[event]) return
this.events[event].forEach(cb => cb(…args))
}
}

// 使用
const emitter = new EventEmitter()
emitter.on('data', (data) => console.log('收到:', data))
emitter.emit('data', { name: 'Alice' })

2.3 区别

对比项观察者模式发布订阅模式
耦合度 直接引用,紧耦合 事件中心解耦
通信方式 目标直接通知观察者 通过事件中心调度
典型应用 Vue 响应式(watcher) EventBus、Redux

// 观察者模式:Vue 响应式
// dep(目标)直接通知 watcher(观察者)

// 发布订阅模式:EventBus
// 通过 eventEmitter 解耦发布者和订阅者


三、策略模式

3.1 定义

定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换。

// 策略对象
const strategies = {
'A': (salary) => salary * 4,
'B': (salary) => salary * 2,
'C': (salary) => salary
}

// 上下文
function calculateBonus(level, salary) {
return strategies[level](salary)
}

calculateBonus('A', 10000) // 40000
calculateBonus('B', 10000) // 20000

3.2 表单验证

// 策略
const validators = {
required: (value) => value !== '' || '此项必填',
minLength: (min) => (value) => value.length >= min || `最少 ${min} 个字符`,
email: (value) => /\\S+@\\S+\\.\\S+/.test(value) || '邮箱格式不正确'
}

// 使用
function validate(value, rules) {
for (const rule of rules) {
const result = rule(value)
if (result !== true) return result
}
return true
}

validate('', [validators.required]) // '此项必填'
validate('ab', [validators.minLength(3)]) // '最少 3 个字符'


四、代理模式 vs 装饰器模式

4.1 代理模式

为对象提供代理以控制对对象的访问。

// 图片懒加载代理
class ImageLoader {
constructor(fileName) {
this.fileName = fileName
}

display() {
console.log(`显示图片: ${this.fileName}`)
}
}

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

display() {
if (!this.realImage) {
this.realImage = new ImageLoader(this.fileName)
}
this.realImage.display()
}
}

// 使用:首次调用才创建真实对象
const image = new ProxyImage('photo.jpg')
image.display() // 创建 ImageLoader 并显示
image.display() // 直接使用缓存的 ImageLoader

4.2 装饰器模式

动态地给对象添加新功能。

// 原始函数
function fetchData() {
console.log('获取数据')
return { data: [1, 2, 3] }
}

// 装饰器:添加日志
function withLogging(fn) {
return function(…args) {
console.log('开始执行')
const result = fn(…args)
console.log('执行结束')
return result
}
}

// 装饰器:添加计时
function withTiming(fn) {
return function(…args) {
const start = Date.now()
const result = fn(…args)
console.log(`耗时: ${Date.now() – start}ms`)
return result
}
}

// 使用
const enhancedFetch = withTiming(withLogging(fetchData))
enhancedFetch()

4.3 区别

对比项代理模式装饰器模式
目的 控制访问 增强功能
接口 保持原接口 保持原接口
典型应用 懒加载、缓存、权限控制 日志、计时、数据转换

五、工厂模式

5.1 简单工厂

class UserFactory {
static create(type) {
switch (type) {
case 'admin':
return { role: 'admin', permissions: ['read', 'write', 'delete'] }
case 'user':
return { role: 'user', permissions: ['read'] }
default:
throw new Error('Unknown user type')
}
}
}

const admin = UserFactory.create('admin')
const user = UserFactory.create('user')

5.2 工厂方法

class Button {
render() {
throw new Error('必须实现 render 方法')
}
}

class PrimaryButton extends Button {
render() {
return '<button class="primary">Primary</button>'
}
}

class SecondaryButton extends Button {
render() {
return '<button class="secondary">Secondary</button>'
}
}

class ButtonFactory {
static create(type) {
switch (type) {
case 'primary': return new PrimaryButton()
case 'secondary': return new SecondaryButton()
default: throw new Error('Unknown button type')
}
}
}

const btn = ButtonFactory.create('primary')
btn.render() // '<button class="primary">Primary</button>'


六、与 Redux、EventBus 的映射

6.1 Redux 中的设计模式

// 1. 单例模式:Store 全局唯一
const store = createStore(rootReducer)

// 2. 发布订阅模式:subscribe / dispatch
store.subscribe(() => {
console.log('状态变化:', store.getState())
})
store.dispatch({ type: 'INCREMENT' })

// 3. 策略模式:reducer 根据 action.type 选择处理逻辑
function counterReducer(state = 0, action) {
switch (action.type) {
case 'INCREMENT': return state + 1
case 'DECREMENT': return state – 1
default: return state
}
}

6.2 EventBus 中的设计模式

// 发布订阅模式
class EventBus {
events = {}

on(event, callback) {
if (!this.events[event]) this.events[event] = []
this.events[event].push(callback)
}

emit(event, …args) {
if (!this.events[event]) return
this.events[event].forEach(cb => cb(…args))
}

off(event, callback) {
if (!this.events[event]) return
this.events[event] = this.events[event].filter(cb => cb !== callback)
}
}

// Vue 2 的 EventBus
const bus = new Vue()
bus.$on('message', (data) => console.log(data))
bus.$emit('message', 'Hello')


七、易混淆点

  • 观察者 vs 发布订阅:观察者模式目标直接通知观察者(紧耦合);发布订阅通过事件中心解耦。
  • 代理 vs 装饰器:代理模式控制访问(懒加载、缓存);装饰器模式增强功能(日志、计时)。
  • 策略 vs 状态:策略模式算法可互换;状态模式行为随状态改变。
  • 单例的实现:闭包、类静态属性、模块导出都可以实现单例。

  • 八、思考与练习

    1. 观察者模式和发布订阅模式的区别是什么?

    解析:

    • 观察者模式:目标直接维护观察者列表,变化时直接通知,紧耦合
    • 发布订阅模式:通过事件中心解耦,发布者和订阅者不直接通信

    2. Redux 使用了哪些设计模式?

    解析:

    • 单例模式:Store 全局唯一
    • 发布订阅模式:subscribe / dispatch
    • 策略模式:reducer 根据 action.type 选择处理逻辑

    3. 如何实现一个简单的 EventBus?

    class EventBus {
    events = {}

    on(event, callback) {
    if (!this.events[event]) this.events[event] = []
    this.events[event].push(callback)
    }

    emit(event, …args) {
    if (!this.events[event]) return
    this.events[event].forEach(cb => cb(…args))
    }

    off(event, callback) {
    if (!this.events[event]) return
    this.events[event] = this.events[event].filter(cb => cb !== callback)
    }
    }

    4. 策略模式和 if-else 的区别是什么?

    解析:

    • if-else:逻辑耦合在一起,难以扩展和维护
    • 策略模式:算法独立封装,可互换、可扩展、易于测试

    5. 代理模式和装饰器模式的区别是什么?

    解析:

    • 代理模式:控制对对象的访问(懒加载、缓存、权限控制)
    • 装饰器模式:动态添加功能(日志、计时、数据转换)

    总结

    • 单例模式:保证一个类只有一个实例(Redux Store、全局管理器)
    • 观察者 vs 发布订阅:前者直接通知紧耦合,后者通过事件中心解耦
    • 策略模式:算法封装可互换(表单验证、计算规则)
    • 代理 vs 装饰器:前者控制访问,后者增强功能
    • 工厂模式:封装对象创建逻辑(组件工厂、用户工厂)
    • Redux:单例 + 发布订阅 + 策略模式的组合
    赞(0)
    未经允许不得转载:171主机测评 » 设计 模式
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址