您现在的位置是:网站首页 > 策略模式(Strategy)的可替换算法实现文章详情
策略模式(Strategy)的可替换算法实现
陈川
【
JavaScript
】
49614人已围观
10074字
策略模式是一种行为设计模式,允许在运行时选择算法的具体实现。通过定义一系列算法并将它们封装成独立的策略类,客户端可以灵活切换不同算法,而不必修改原有代码结构。这种模式特别适合处理需要频繁变更或扩展的算法逻辑场景。
策略模式的核心思想
策略模式的核心在于将算法与使用算法的上下文分离。它包含三个主要角色:
- Context(上下文):维护对策略对象的引用,负责将客户端请求委托给当前策略。
- Strategy(策略接口):定义所有具体策略必须实现的公共接口。
- ConcreteStrategy(具体策略):实现策略接口的具体算法类。
这种分离使得算法可以独立于客户端变化,符合开闭原则——对扩展开放,对修改关闭。
JavaScript中的策略实现
在JavaScript中,由于函数是一等公民,策略模式可以非常简洁地实现。以下是典型的实现方式:
// 策略接口(隐式)
const strategies = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
multiply: (a, b) => a * b
};
// 上下文
class Calculator {
constructor(strategy) {
this.strategy = strategy;
}
execute(a, b) {
return this.strategy(a, b);
}
setStrategy(strategy) {
this.strategy = strategy;
}
}
// 使用示例
const calc = new Calculator(strategies.add);
console.log(calc.execute(5, 3)); // 8
calc.setStrategy(strategies.multiply);
console.log(calc.execute(5, 3)); // 15
表单验证的实际案例
策略模式特别适合表单验证场景,不同字段可能需要不同的验证规则:
const validationStrategies = {
isNonEmpty: (value, errMsg) => {
if (value === '') return errMsg;
},
minLength: (value, length, errMsg) => {
if (value.length < length) return errMsg;
},
isMobile: (value, errMsg) => {
if (!/^1[3-9]\d{9}$/.test(value)) return errMsg;
}
};
class Validator {
constructor() {
this.cache = [];
}
add(value, rules) {
rules.forEach(rule => {
const strategyArr = rule.strategy.split(':');
const strategyName = strategyArr.shift();
strategyArr.unshift(value);
strategyArr.push(rule.errMsg);
this.cache.push(() =>
validationStrategies[strategyName].apply(null, strategyArr)
);
});
}
validate() {
for (let i = 0; i < this.cache.length; i++) {
const msg = this.cache[i]();
if (msg) return msg;
}
}
}
// 使用示例
const validator = new Validator();
validator.add('13812345678', [
{ strategy: 'isNonEmpty', errMsg: '手机号不能为空' },
{ strategy: 'isMobile', errMsg: '手机号格式不正确' }
]);
console.log(validator.validate()); // undefined表示验证通过
电商促销策略应用
电商平台经常需要切换不同的促销策略,策略模式可以优雅地处理这种需求:
const promotionStrategies = {
normal: originalPrice => originalPrice,
discount: (originalPrice, discount) => originalPrice * discount,
fullReduction: (originalPrice, condition, reduction) => {
return originalPrice >= condition ? originalPrice - reduction : originalPrice;
}
};
class PriceCalculator {
constructor(strategy, ...args) {
this.strategy = strategy;
this.args = args;
}
calculate(price) {
return this.strategy(price, ...this.args);
}
}
// 使用示例
const normalPrice = new PriceCalculator(promotionStrategies.normal);
console.log(normalPrice.calculate(100)); // 100
const discountPrice = new PriceCalculator(promotionStrategies.discount, 0.8);
console.log(discountPrice.calculate(100)); // 80
const fullReductionPrice = new PriceCalculator(
promotionStrategies.fullReduction,
200,
50
);
console.log(fullReductionPrice.calculate(250)); // 200
策略模式的进阶应用
动态策略组合
策略可以组合使用,形成更复杂的业务逻辑:
const imageProcessingStrategies = {
grayscale: image => `Applying grayscale to ${image}`,
resize: (image, width, height) =>
`Resizing ${image} to ${width}x${height}`,
watermark: (image, text) =>
`Adding watermark "${text}" to ${image}`
};
class ImageProcessor {
constructor() {
this.strategies = [];
}
addStrategy(strategy, ...args) {
this.strategies.push({ strategy, args });
}
process(image) {
return this.strategies.reduce((result, { strategy, args }) => {
return strategy(result, ...args);
}, image);
}
}
// 使用示例
const processor = new ImageProcessor();
processor.addStrategy(imageProcessingStrategies.resize, 800, 600);
processor.addStrategy(imageProcessingStrategies.grayscale);
processor.addStrategy(imageProcessingStrategies.watermark, 'Confidential');
console.log(processor.process('photo.jpg'));
// "Adding watermark "Confidential" to Resizing Applying grayscale to photo.jpg to 800x600"
策略工厂模式
结合工厂模式可以更灵活地创建策略:
class StrategyFactory {
static createStrategy(type) {
switch(type) {
case 'express':
return (weight) => weight * 5 + 15;
case 'standard':
return (weight) => weight * 3;
case 'economy':
return (weight) => weight * 2 - (weight > 10 ? 5 : 0);
default:
throw new Error('Unknown strategy type');
}
}
}
class ShippingCalculator {
constructor(strategyType) {
this.strategy = StrategyFactory.createStrategy(strategyType);
}
calculate(weight) {
return this.strategy(weight);
}
}
// 使用示例
const expressShipping = new ShippingCalculator('express');
console.log(expressShipping.calculate(5)); // 40
策略模式的优缺点
优势体现
- 算法复用:相同算法可以在不同上下文中复用
- 避免条件判断:替代大量的if-else或switch-case语句
- 扩展性强:新增策略不会影响现有代码
- 运行时切换:可以动态改变对象的行为
潜在缺点
- 策略类增多:每个策略都需要一个单独类/函数
- 客户端认知成本:需要了解不同策略的区别
- 通信开销:策略与上下文之间可能需要传递额外数据
与其它模式的比较
策略模式 vs 状态模式
虽然结构相似,但两者意图不同:
- 策略模式中策略是主动选择的
- 状态模式中状态转换通常由内部条件触发
策略模式 vs 工厂模式
工厂关注对象创建,策略关注行为委托:
// 工厂模式示例
class LoggerFactory {
createLogger(type) {
switch(type) {
case 'file': return new FileLogger();
case 'console': return new ConsoleLogger();
}
}
}
// 策略模式示例
const loggingStrategies = {
file: message => saveToFile(message),
console: message => console.log(message)
};
class Logger {
constructor(strategy) {
this.strategy = strategy;
}
log(message) {
this.strategy(message);
}
}
TypeScript中的策略模式实现
使用TypeScript可以更好地表达策略接口:
interface SortingStrategy<T> {
sort(items: T[]): T[];
}
class QuickSortStrategy<T> implements SortingStrategy<T> {
sort(items: T[]): T[] {
// 快速排序实现
return [...items].sort((a, b) => a > b ? 1 : -1);
}
}
class MergeSortStrategy<T> implements SortingStrategy<T> {
sort(items: T[]): T[] {
// 归并排序实现
if (items.length <= 1) return items;
const mid = Math.floor(items.length / 2);
return this.merge(
this.sort(items.slice(0, mid)),
this.sort(items.slice(mid))
);
}
private merge(left: T[], right: T[]): T[] {
// 合并逻辑
}
}
class Sorter<T> {
constructor(private strategy: SortingStrategy<T>) {}
sort(items: T[]): T[] {
return this.strategy.sort(items);
}
setStrategy(strategy: SortingStrategy<T>) {
this.strategy = strategy;
}
}
// 使用示例
const numbers = [3, 1, 4, 1, 5, 9];
const sorter = new Sorter(new QuickSortStrategy<number>());
console.log(sorter.sort(numbers));
sorter.setStrategy(new MergeSortStrategy());
console.log(sorter.sort(numbers));
前端框架中的策略模式实践
React中的策略应用
在React组件中使用策略模式处理不同渲染逻辑:
const renderStrategies = {
list: items => (
<ul>
{items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
),
grid: items => (
<div className="grid">
{items.map(item => (
<div key={item.id} className="grid-item">{item.name}</div>
))}
</div>
),
table: items => (
<table>
<tbody>
{items.map(item => (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.name}</td>
</tr>
))}
</tbody>
</table>
)
};
function ItemDisplay({ items, displayType }) {
const render = renderStrategies[displayType] || renderStrategies.list;
return (
<div className="item-container">
{render(items)}
</div>
);
}
// 使用示例
<ItemDisplay
items={products}
displayType="grid"
/>
Vue中的策略应用
Vue的计算属性很适合实现策略模式:
const paymentStrategies = {
creditCard: amount => amount * 1.03, // 3%手续费
paypal: amount => amount + 2, // 固定$2手续费
bankTransfer: amount => amount // 无手续费
};
new Vue({
el: '#app',
data: {
amount: 100,
paymentMethod: 'creditCard'
},
computed: {
finalAmount() {
return paymentStrategies[this.paymentMethod](this.amount);
}
}
});
性能优化中的策略选择
根据运行环境选择最优策略:
const renderingStrategies = {
canvas: {
render: (data) => {
// Canvas渲染实现
console.log(`Rendering ${data.length} items with Canvas`);
},
isSupported: () => !!document.createElement('canvas').getContext
},
svg: {
render: (data) => {
// SVG渲染实现
console.log(`Rendering ${data.length} items with SVG`);
},
isSupported: () => true
},
dom: {
render: (data) => {
// DOM渲染实现
console.log(`Rendering ${data.length} items with DOM`);
},
isSupported: () => true
}
};
class DataRenderer {
constructor(data) {
this.data = data;
this.determineBestStrategy();
}
determineBestStrategy() {
if (this.data.length > 1000 && renderingStrategies.canvas.isSupported()) {
this.strategy = renderingStrategies.canvas;
} else if (this.data.length > 100 && renderingStrategies.svg.isSupported()) {
this.strategy = renderingStrategies.svg;
} else {
this.strategy = renderingStrategies.dom;
}
}
render() {
this.strategy.render(this.data);
}
}
// 使用示例
const largeData = new Array(1500).fill(0).map((_, i) => i);
const renderer = new DataRenderer(largeData);
renderer.render(); // 自动选择Canvas策略
测试中的策略应用
针对不同环境使用不同的测试策略:
const testingStrategies = {
unit: {
run: (tests) => tests.forEach(test => console.log(`Running unit test: ${test}`)),
needsMock: true
},
integration: {
run: (tests) => tests.forEach(test => console.log(`Running integration test: ${test}`)),
needsMock: false
},
e2e: {
run: (tests) => tests.forEach(test => console.log(`Running e2e test: ${test}`)),
needsMock: false
}
};
class TestRunner {
constructor(strategyType) {
this.strategy = testingStrategies[strategyType];
}
runTests(tests) {
if (this.strategy.needsMock) {
console.log('Setting up mocks...');
}
this.strategy.run(tests);
}
}
// 使用示例
const unitTestRunner = new TestRunner('unit');
unitTestRunner.runTests(['UserService', 'AuthService']);