常见设计模式误用与反模式
设计模式误用的典型场景
设计模式本为解决特定问题而生,但机械套用往往适得其反。Singleton模式在JavaScript中的滥用尤为常见,开发者常将模块全局化当作单例实现:
// 反模式示例:伪单例
const pseudoSingleton = {
data: [],
add(item) {
this.data.push(item)
}
}
// 模块A修改数据
pseudoSingleton.add('A')
// 模块B意外覆盖数据
pseudoSingleton.data = null
这种实现缺乏真正的实例控制,无法阻止直接修改内部状态。更合理的做法应使用闭包保护实例:
const RealSingleton = (() => {
let instance
class Singleton {
constructor() {
this.data = []
}
add(item) {
this.data.push(item)
}
}
return {
getInstance() {
if (!instance) {
instance = new Singleton()
}
return instance
}
}
})()
观察者模式的过度订阅陷阱
事件驱动架构中,观察者模式常因未及时取消订阅导致内存泄漏。典型场景如SPA中的全局事件总线:
// 危险实现
class EventBus {
listeners = {}
on(event, callback) {
if (!this.listeners[event]) {
this.listeners[event] = []
}
this.listeners[event].push(callback)
}
emit(event, data) {
(this.listeners[event] || []).forEach(cb => cb(data))
}
}
// 组件内使用
mounted() {
eventBus.on('dataUpdate', this.handleData)
}
// 组件销毁时忘记取消订阅 -> 内存泄漏
改进方案应强制要求提供取消订阅机制:
class SafeEventBus {
listeners = new Map()
on(event, callback) {
const callbacks = this.listeners.get(event) || new Set()
callbacks.add(callback)
this.listeners.set(event, callbacks)
return () => this.off(event, callback) // 返回取消函数
}
off(event, callback) {
const callbacks = this.listeners.get(event)
if (callbacks) {
callbacks.delete(callback)
}
}
}
策略模式的条件爆炸
策略模式本应简化条件分支,但不当实现会导致策略类膨胀:
// 反模式:策略类包含过多条件
class PaymentStrategy {
pay(method, amount) {
switch(method) {
case 'alipay':
return this.processAlipay(amount)
case 'wechat':
return this.processWeChat(amount)
case 'creditCard':
return this.processCreditCard(amount)
// 新增支付方式需要修改类
}
}
}
应拆分为独立策略对象:
const strategies = {
alipay: (amount) => { /* 具体实现 */ },
wechat: (amount) => { /* 具体实现 */ },
creditCard: (amount) => { /* 具体实现 */ }
}
function processPayment(method, amount) {
return strategies[method]?.(amount) ?? defaultStrategy(amount)
}
装饰器模式的性能代价
装饰器模式滥用会导致调用栈过深,特别是在React高阶组件中:
// 多层装饰导致组件树难以调试
const EnhancedComponent = withRouter(
connect(mapStateToProps)(
withStyles(styles)(
memo(BaseComponent)
)
)
)
现代React更推荐使用Hook组合:
function SmartComponent() {
const router = useRouter()
const data = useSelector(mapStateToProps)
const classes = useStyles(styles)
return <BaseComponent {...props} />
}
工厂方法的过度抽象
工厂方法在简单对象创建场景反而增加复杂度:
// 不必要的工厂
class ButtonFactory {
createButton(type) {
switch(type) {
case 'primary':
return new PrimaryButton()
case 'secondary':
return new SecondaryButton()
default:
return new DefaultButton()
}
}
}
// 直接实例化更清晰
const buttonMap = {
primary: <PrimaryButton />,
secondary: <SecondaryButton />,
default: <DefaultButton />
}
function Button({ type = 'default' }) {
return buttonMap[type] || buttonMap.default
}
命令模式的过度封装
将简单操作封装为命令对象反而降低可读性:
// 过度设计的命令模式
class Command {
execute() {}
}
class SaveCommand extends Command {
constructor(receiver) {
this.receiver = receiver
}
execute() {
this.receiver.save()
}
}
// 使用时
const command = new SaveCommand(editor)
command.execute()
// 直接调用更清晰
editor.save()
仅在需要撤销/重做等高级功能时才适用命令模式。
适配器模式的接口污染
适配器可能掩盖底层接口设计问题:
// 反模式:用适配器掩盖设计缺陷
class BadAPI {
getData() {
return fetch('/legacy-endpoint')
.then(res => res.json())
.then(data => ({
items: data.records,
meta: data.info
}))
}
}
// 更好的做法是直接修复API设计
class GoodAPI {
async getItems() {
const res = await fetch('/modern-endpoint')
return res.json()
}
}
模板方法模式的继承陷阱
传统实现方式导致子类与父类强耦合:
// 脆弱的模板方法
class DataProcessor {
process() {
this.validate()
this.transform()
this.save()
}
validate() { /* 默认实现 */ }
transform() { /* 抽象方法 */ }
save() { /* 默认实现 */ }
}
// 改用组合策略更灵活
function createProcessor({ validate, transform, save }) {
return {
process() {
validate?.()
transform()
save?.()
}
}
}
状态模式的状态爆炸
复杂状态机可能导致状态类数量激增:
// 状态类过多
class Order {
state = new DraftState(this)
setState(state) {
this.state = state
}
}
class DraftState { /* ... */ }
class PendingState { /* ... */ }
class PaidState { /* ... */ }
class ShippedState { /* ... */ }
class CancelledState { /* ... */ }
可考虑状态表驱动:
const stateMachine = {
draft: {
submit: 'pending',
cancel: 'cancelled'
},
pending: {
pay: 'paid',
cancel: 'cancelled'
},
paid: {
ship: 'shipped'
}
}
function transition(state, action) {
return stateMachine[state]?.[action] || state
}
中介者模式的上帝对象
中介者可能演变为过度集中的控制中心:
// 承担过多职责的中介者
class ChatRoom {
constructor() {
this.users = []
}
register(user) {
this.users.push(user)
user.room = this
}
send(message, from) {
this.users.forEach(user => {
if (user !== from) {
user.receive(message)
}
})
}
// 逐渐添加更多业务逻辑...
kickUser() {}
changeTopic() {}
setRules() {}
}
应遵循单一职责原则拆分功能:
class UserManager {
addUser() {}
removeUser() {}
}
class MessageDispatcher {
broadcast() {}
privateMessage() {}
}
class RoomPolicy {
setRules() {}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:设计模式与代码可维护性的关系
下一篇:如何选择合适的设计模式