悠悠楠杉
JavaScript闭包实现策略模式:构建灵活可扩展的代码架构
JavaScript闭包实现策略模式:构建灵活可扩展的代码架构
策略模式的核心思想与闭包优势
策略模式(Strategy Pattern)是一种将算法封装在独立对象中的设计模式,允许客户端在运行时灵活切换算法实现。而JavaScript闭包(Closure)通过保留函数创建时的词法环境,为策略模式提供了天然的实现载体。
闭包实现策略模式的三大优势:
1. 状态封装:闭包可以维持策略执行时的私有状态
2. 接口统一:所有策略对象保持相同的方法签名
3. 无污染全局:避免策略实现污染全局命名空间
基础实现:表单验证策略案例
javascript
// 策略容器
const validationStrategies = (function() {
// 私有策略集合
const strategies = {
isNonEmpty: function(value, errMsg) {
if(value === '') return errMsg
},
minLength: function(value, length, errMsg) {
if(value.length < length) return errMsg
},
isMobile: function(value, errMsg) {
if(!/^1[3-9]\d{9}$/.test(value)) return errMsg
}
}
return {
// 统一执行接口
validate: function(type, ...args) {
return strategies[type] && strategiestype
},
// 动态添加策略
addStrategy: function(name, fn) {
strategies[name] = fn
}
}
})()
// 使用示例
const errorMsg = validationStrategies.validate(
'isMobile',
'13812345678',
'手机号格式错误'
)
进阶应用:电商促销策略系统
javascript
function createPromotionStrategy() {
// 策略缓存
const strategies = new Map()
// 上下文状态
let currentStrategy = 'normal'
let history = []
// 注册策略
function register(name, strategyFn) {
strategies.set(name, strategyFn)
return this // 支持链式调用
}
// 执行当前策略
function execute(price) {
const result = strategies.has(currentStrategy)
? strategies.get(currentStrategy)(price)
: price
history.push({
strategy: currentStrategy,
input: price,
output: result,
timestamp: Date.now()
})
return result
}
// 切换策略
function useStrategy(name) {
if(strategies.has(name)) {
currentStrategy = name
}
return this
}
return {
register,
execute,
useStrategy,
getHistory: () => [...history],
getCurrentStrategy: () => currentStrategy
}
}
// 初始化策略系统
const promotion = createPromotionStrategy()
.register('discount', price => price * 0.8)
.register('fullReduce', price => price > 200 ? price - 50 : price)
.register('newUser', price => price * 0.5)
// 运行时动态切换策略
promotion.useStrategy('fullReduce')
console.log(promotion.execute(250)) // 输出200
性能优化与内存管理
- 策略缓存:使用WeakMap存储不常用的策略,避免内存泄漏
- 惰性加载:按需加载策略实现而非初始化时全部加载
- 策略复用:通过原型共享相同策略的多个实例
javascript
const advancedStrategy = (function() {
const coreStrategies = {
// 基础策略...
}
const lazyStrategies = new WeakMap()
function loadExternalStrategy(key) {
// 模拟异步加载
return import(./strategies/${key}.js
)
.then(module => {
lazyStrategies.set(key, module.default)
return module.default
})
}
return {
async execute(key, ...args) {
if(coreStrategies[key]) {
return coreStrategieskey
}
if(lazyStrategies.has(key)) {
return lazyStrategies.get(key)(...args)
}
const strategy = await loadExternalStrategy(key)
return strategy(...args)
}
}
})()
与模块系统的结合实践
在现代前端工程中,闭包策略模式可以与ES Modules完美结合:
javascript
// strategies/discount.js
let executionCount = 0
export default function discountStrategy(price, ratio = 0.9) {
executionCount++
console.log(折扣策略已执行${executionCount}次
)
return price * ratio
}
// strategyContext.js
export function createStrategyContext() {
const strategies = new Map()
async function loadStrategy(modulePath) {
try {
const module = await import(modulePath)
strategies.set(module.name || modulePath, module.default)
} catch(e) {
console.error('策略加载失败:', e)
}
}
function getStrategy(name) {
return strategies.get(name)
}
return {
loadStrategy,
getStrategy
}
}
单元测试策略模式的最佳实践
javascript
describe('闭包策略模式测试套件', () => {
let strategySystem
beforeEach(() => {
strategySystem = createPromotionStrategy()
.register('double', n => n * 2)
.register('square', n => n * n)
})
test('应正确执行注册策略', () => {
strategySystem.useStrategy('double')
expect(strategySystem.execute(3)).toBe(6)
strategySystem.useStrategy('square')
expect(strategySystem.execute(3)).toBe(9)
})
test('应记录策略执行历史', () => {
strategySystem.useStrategy('double').execute(5)
strategySystem.useStrategy('square').execute(4)
const history = strategySystem.getHistory()
expect(history).toHaveLength(2)
expect(history[0].output).toBe(10)
expect(history[1].output).toBe(16)
})
})
浏览器环境下的应用场景
- 表单验证系统:根据字段类型动态切换验证规则
- 动画引擎:不同的缓动函数作为独立策略
- 请求处理:针对不同API版本采用不同的参数处理策略
- UI渲染:根据设备类型选择不同的布局策略
javascript
// 响应式布局策略示例
const layoutStrategies = (() => {
const strategies = {
mobile: () => {
document.body.classList.add('mobile-layout')
// 移动端特定逻辑...
},
desktop: () => {
document.body.classList.add('desktop-layout')
// 桌面端特定逻辑...
}
}
function detectDeviceType() {
return window.innerWidth < 768 ? 'mobile' : 'desktop'
}
return {
applyLayout() {
const type = detectDeviceType()
strategiestype
}
}
})()
// 监听窗口变化
window.addEventListener('resize', debounce(layoutStrategies.applyLayout, 300))
Node.js中的特殊应用场景
- 中间件处理:不同路由采用不同的认证策略
- 日志系统:根据环境切换日志记录策略
- 数据库访问:多数据源切换策略
- 文件处理:不同文件类型的解析策略
javascript
// 多数据库切换策略
function createDBStrategy(config) {
const drivers = new Map()
let currentDriver = 'mysql'
async function connect(name = currentDriver) {
if(!drivers.has(name)) {
const driver = require(./drivers/${name}
)
drivers.set(name, await driver.createConnection(config[name]))
}
currentDriver = name
return drivers.get(name)
}
return {
connect,
query(sql, params = []) {
return drivers.get(currentDriver).query(sql, params)
},
switchTo(name) {
if(drivers.has(name)) {
currentDriver = name
}
return this
}
}
}
// 使用示例
const db = createDBStrategy({
mysql: { /* 配置 / },
pg: { / 配置 */ }
})
await db.connect('pg').query('SELECT * FROM users')