ruby和javascript的策略模式
来自官方
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
结构图
ruby中的简单实现(代码来自《ruby设计模式》)
# 根据鸭子模型, 没有让策略对象继承一个提供统一接口的基类
# 策略1
class HTMLFormatter
def output_report title, text
puts ‘<html>‘
puts ‘ <head>‘
puts ‘ <title>‘ + title + ‘</title>‘
puts ‘ </head>‘
puts ‘ <body>‘
text.each do |line|
puts "<p>#{line}</p>"
end
puts ‘ </body>‘
puts ‘</html>‘
end
end
# 策略2
class PlainTextFormatter
def output_report title, text
puts ‘******** ‘ + title + ‘ ********‘
text.each do |line|
puts line
end
end
end
# 这边之所以用策略模式来做,是因为Reporter有可能会将text打印成不同格式的文本,如果将来需要
# 支持xml那么只需要增加一个支持output_report接口的类就可以了
class Reporter
attr_reader :title, :text
attr_accessor :formater
def initialize formater
@title = ‘My Report‘
@text = [‘This is my report‘, ‘Please see the report‘, ‘It is ok‘]
@formater = formater
end
# 策略对象具有共同的接口
def output_report
@formater.output_report @title, @text
end
end
# 环境对象调用不同的策略
Reporter.new(HTMLFormatter.new).output_report
Reporter.new(PlainTextFormatter.new).output_report
那么策略模式到底可以在什么时候用呢?
我们先来一个例子,一般情况下,如果我们要做数据合法性验证,很多时候都是按照swith语句来判断,但是这就带来几个问题,首先如果增加需求的话,我们还要再次修改这段代码以增加逻辑,而且在进行单元测试的时候也会越来越复杂,代码如下:
# 在最早的时候我差不多就是用这样的方式去做表单的数据验证的
validator = {
validate: function (value, type) {
switch (type) {
case ‘isNonEmpty ‘:
{
return true; // NonEmpty 验证结果
}
case ‘isNumber ‘:
{
return true; // Number 验证结果
break;
}
case ‘isAlphaNum ‘:
{
return true; // AlphaNum 验证结果
}
default:
{
return true;
}
}
}
};
// 测试
alert(validator.validate("123", "isNonEmpty"));
ok, 用策略模式去改造一下, 首先分析一下validator是一个策略环境,不同的验证就是一个策略对象,我们需要做的就是让不同的策略对象支持统一的接口
重构代码如下:
var validator = {
types: {},
messages: [],
config: {},
validate: function (data) {
var i, msg, type, checker, result_ok;
this.messages = [];
for (i in data) {
if (data.hasOwnProperty(i)) {
type = this.config[i]; // 根据key查询是否有存在的验证规则
checker = this.types[type]; // 获取验证规则的验证类
if (!type) {
continue; // 如果验证规则不存在,则不处理
}
if (!checker) { // 如果验证规则类不存在,抛出异常
throw {
name: "ValidationError",
message: "No handler to validate type " + type
};
}
# 开始执行按需执行不同的策略
result_ok = checker.validate(data[i]); // 使用查到到的单个验证类进行验证
if (!result_ok) {
msg = "Invalid value for *" + i + "*, " + checker.instructions;
this.messages.push(msg);
}
}
}
return this.hasErrors();
},
hasErrors: function () {
return this.messages.length !== 0;
}
};
// 验证给定的值是否不为空
validator.types.isNonEmpty = {
validate: function (value) {
return value !== "";
},
instructions: "传入的值不能为空"
};
// 验证给定的值是否是数字
validator.types.isNumber = {
validate: function (value) {
return !isNaN(value);
},
instructions: "传入的值只能是合法的数字,例如:1, 3.14 or 2010"
};
// 验证给定的值是否只是字母或数字
validator.types.isAlphaNum = {
validate: function (value) {
return !/[^a-z0-9]/i.test(value);
},
instructions: "传入的值只能保护字母和数字,不能包含特殊字符"
};
var data = {
first_name: "Tom",
last_name: "Xu",
age: "unknown",
username: "TomXu"
};
validator.config = {
first_name: ‘isNonEmpty‘,
age: ‘isNumber‘,
username: ‘isAlphaNum‘
};
validator.validate(data);
if (validator.hasErrors()) {
console.log(validator.messages.join("\n"));
}
早期的时候看到这样的代码我只想说fuck, 明明很简单的东西,非搞的这么复杂有必要么?
当然, 假设项目很简单,用重构前的代码OK, 而且这里也只是去举个例子
那么同样的需求用ruby如何实现呢?
代码如下:(这里我完全是用javascript的思维来写ruby了, 不知道阅读性有没有问题)
# coding: utf-8
# 策略1
class IsNonEmpty
def self.check(data)
data.nil?
end
def self.notice
"传入的值不能为空\n"
end
end
# 策略2
class IsAlphaNum
def self.check(data)
data =~ /[^a-z0-9]/
end
def self.notice
"传入的值只能保护字母和数字,不能包含特殊字符\n"
end
end
# 环境
class Validate
attr_reader :msg
def initialize
# @types = []
@msg = ‘‘
# @config = {}
end
# 这个地方没有再按照javascript的逻辑去写,这边如果需要不同的实例支持不同的验证的话,
# 可以将策略对象添加到@types中
# @config 同理
# def add_types(type)
# @types << type
# end
def check(hash_data)
hash_data.each_pair do |key, val|
@msg << "#{key}errors #{val[:type].notice}" if val[:type].check(val[:data])
end
end
def errors
!@msg.nil?
end
end
validate = Validate.new
hash_data = {
first: {type: IsNonEmpty, data: 1324},
second: {type: IsNonEmpty, data: nil},
third: {type: IsAlphaNum, data: ‘123angel‘},
forth: {type: IsAlphaNum, data: ‘123sf@/!‘}
}
validate.check(hash_data)
if validate.errors
puts validate.msg
end
最后: 本打算总结出ruby和javascript共享接口的不同, 奈何能力有限,只能意会出来却不能言传出来
也可能是为没有理解出其中的关键点
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。