一. 策略模式的定义
策略模式的定义:定义了算法家族,分别封装起来,让它们之间可以相互替换。
策略模式的目的是想将算法的使用与实现分离开来。
二. 策略模式的实现
究其本质,策略模式使用委托去解耦使用它们的算法类。
基于策略模式的程序至少由两部分组成:
第一部分是一组 策略类。策略类封装了具体的算法,并负责具体的计算过程。
第二部分是 环境类 Context。Context接受客户请求,并将请求委托给某一个策略类。
例如:
// 定义一组策略类
var strategies = {
A: function() { // A策略内容... },
B: function() { // B策略内容... },
C: function() { // C策略内容... }
};
// 定义Context环境类,维持对策略对象的引用
var context = function(strategy) {
// 对strategy的一些处理 ... ...
return strategy;
};
// 实际调用
context( strategies.A() );
context( strategies.B() );
context( strategies.C() );
这样做有几个好处:可以防止使用大条件语句来决定哪些算法用于特定类型的对象,将关注点分离开来,因此降低了客户端的复杂度,同时还可以促进子类化的组成,提高了模块化和可测性。
2.1 实例:使用策略模式实现图片像素操作
var filterRules = {
// 灰度
gray: function(imagedata) {
var imagedata = imagedata || [],
pixels = imagedata.data,
pixelsLen = pixels.length;
for(var i = 0; i < pixelsLen; i++) {
pixels[i * 4] = 255 - pixels[i * 4]; // 红色
pixels[i * 4 + 1] = 255 - pixels[i * 4 + 1]; // 绿色
pixels[i * 4 + 2] = 255 - pixels[i * 4 + 2]; // 蓝色
}
},
// 反转颜色
invert: function(imagedata) {
var imagedata = imagedata || [],
pixels = imagedata.data,
pixelsLen = pixels.length;
for(var i = 0; i < pixelsLen; i++) {
var average = ( pixels[i * 4] + pixels[i * 4 + 1] + pixels[i * 4 + 2] ) / 3;
pixels[i * 4] = average; // 红色
pixels[i * 4 + 1] = average; // 绿色
pixels[i * 4 + 2] = average; // 蓝色
}
},
// 浮雕
emboss: function(imagedata) {
var imagedata = imagedata || [],
pixels = imagedata.data,
pixelsLen = pixels.length,
imageWidth = imagedata.width;
for(var i = 0; i < pixelsLen; i++) {
if(i < pixelsLen - imageWidth * 4) {
if((i + 1) % 4 !== 0) {
if((i + 4) % (imageWidth * 4) == 0) {
pixels[i] = pixels[i - 4];
pixels[i + 1] = pixels[i - 3];
pixels[i + 2] = pixels[i - 2];
pixels[i + 3] = pixels[i - 1];
i += 4;
} else {
pixels[i] = 255 / 2 + 2 * pixels[i] - pixels[i + 4] - pixels[i + imageWidth * 4];
}
}
} else if((i + 1) % 4 !== 0) {
pixels[i] = pixels[i - imageWidth * 4];
}
}
}
};
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
image = new Image();
image.src = "1.jpg";
image.onload = function() {
context.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height);
// renderFilter(context, filter.gray, canvas);
};
// 渲染滤镜
function renderFilter(context, filter, canvas) {
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
if(filterRules[filter]) {
filterRules[filter](imageData);
}
context.putImageData(imageData, 0, 0);
}
// 选择滤镜模式
var filterBtn = document.getElementById("btn_filter");
filterBtn.addEventListener("click", function(e) {
var target = e.target,
filter = target.dataset.filter;
if(filterRules[filter]) {
renderFilter(context, filter, canvas);
}
}, false);
效果如下:
【正在施工中… …】
2.2 实例:使用策略模式实现表单校验
参考《JavaScript设计模式与开发实践》第五章P80~86。
三. 策略模式的优缺点
3.1 策略模式的优点:
策略模式利用组合、委托和多态等技术和思想,可以有效地避免使用多重条件判断;
算法独立封装,使得他们可以自由切换,易于理解和扩展;
复用性高。
3.2 策略模式的缺点:
策略类会增多;
所有策略类都需要对外暴露。
四. 策略模式的适用场景
策略模式的适用场景有:
当系统中有许多类,而他们之间的区别仅在于行为时,那么使用策略模式可以动态地让一个对象在许多行为中选择一种;
当系统需要动态地在几种算法中选择一种时;
一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。
五. 总结
策略模式将算法的使用和算法的实现分离开来,将不变的部分和变化的部分分隔开,从而使程序进行解耦。
策略模式对于可提炼出多种较通用的算法,并在不同的使用场景下按需选择某一种或某几种策略完成对应的业务逻辑比较适用。所以,当业务逻辑涉及到分类和按需应用时,可考虑使用策略模式来实现。