策略模式主要解决在有多种情况下,使用 if...else 所带来的复杂和难以维护。
它的优点是算法可以自由切换,同时可以避免多重if...else判断,且具有良好的扩展性。
我们有一个根据不同的类型返回不同价格的一个方法
function getPrice (type) {
if (type === 1) {
// code 或许每个分支要处理很多逻辑
}
if (type === 2) {
// code
}
if (type === 3) {
// code
}
if (type === 4) {
// code
}
if (type === 5) {
// code
}
if (type === 6) {
// code
}
if (type === 7) {
// code
}
}
从代码上看确实没有什么问题,但是如果需要增加一种新的类型,我们就会一个if判断,导致这个方法可能会过于庞大,后期不太好维护。
其次代码每次都是从上往下走,可能存在前面某个判断出现问题(如某个 && 的判断变量null 之类),导致后面的代码没有走,所以这种影响还是挺大的。
用了这么多 if-else,我们的目的是什么?是不是就是为了把传进来的参数的值-对应的处理函数,这个映射关系给明确下来?在 JS 中我们可以通过对象映射的形式来做,如下代码
/*
1、把 if else 的代码快优化为一个一个的映射
2、把if else 里面的逻辑抽离成一个独立的函数,这样方便其他模块或者分支使用
*/
function getPrice (type) {
const actionMap = {
'1': action1,
'2': action2,
'3': action3,
'4': action4,
'5': action5,
'6': action6,
'7': action7,
}
const params = {}
return actionMap[type](params)
}
这种代码结构变得易读、易维护。
这就是最简单的策略模式
如果不把逻辑封装起来,那么我们在判断的时候会写很多的if else,如写一个很简单的表单的校验
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="telephone=no,email=no" name="format-detection">
<meta name="App-Config" content="fullscreen=yes,useHistoryState=yes,transition=yes">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<div>
<form action="" id="form">
姓名:<input type="text" id="username"><br>
密码:<input type="password" id="password1"><br>
确认密码:<input type="password" id="password2"><br>
手机号:<input type="text" id="phone"><br>
<input type="submit" value="提交">
</form>
</div>
<script>
function getValue (id) {
return document.getElementById(id).value;
}
var formData = document.getElementById('form')
formData.onsubmit = function () {
var name = getValue('username');
var pwd1 = getValue('password1');
var pwd2 = getValue('password2');
var tel = getValue('phone');
if (name.replace(/(^\s*)|(\s*$)/g, "") === "") {
alert('用户名不能为空')
return false
}
if (pwd1.replace(/(^\s*)|(\s*$)/g, "") === "") {
alert('密码不能为空')
return false
}
if (pwd2.replace(/(^\s*)|(\s*$)/g, "") === "") {
alert('确认密码不能为空')
return false
}
if (pwd2 !== pwd1) {
alert('确认密码与原密码不相同!')
return false
}
if (tel.replace(/(^\s*)|(\s*$)/g, "") === "") {
alert('手机号码不能为空')
return false
}
if (!/^1[3,4,5,7,8,9][0-9]\d{8}$/.test(tel)) {
alert('手机号码格式不正确')
return false
}
alert('注册成功')
}
</script>
</body>
</html>
只是4个字段,我们用了 6个if判断来做相关的逻辑校验。
仔细观察发现很多校验的逻辑是一致的,所以我们可以把他封装起来,用策略模式修改成如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="telephone=no,email=no" name="format-detection">
<meta name="App-Config" content="fullscreen=yes,useHistoryState=yes,transition=yes">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<div>
<form action="" id="form">
姓名:<input type="text" id="username"><br>
密码:<input type="password" id="password1"><br>
确认密码:<input type="password" id="password2"><br>
手机号:<input type="text" id="phone"><br>
<input type="submit" value="提交">
</form>
</div>
<script>
let formData = document.getElementById('form')
function getValue(id) {
return document.getElementById(id).value;
}
function Validate() { }
Validate.prototype.rules = {
// 是否手机号
isMobile: function (str) {
let rule = /^1[3,4,5,7,8,9][0-9]\d{8}$/;
return rule.test(str);
},
// 是否必填
isRequired: function (str) {
// 除去首尾空格
let value = str.replace(/(^\s*)|(\s*$)/g, "");
return value !== "";
},
// 最小长度
minLength: function (str, length) {
let strLength = str.length;
return strLength >= length;
},
// 是否相等
isEqual: function (...args) {
let equal = args.every(function (value) {
return value === args[0];
})
return equal;
}
}
Validate.prototype.test = function (rules) {
let _this = this;
let valid; // 保存校验结果
for (let key in rules) { // 遍历校验规则对象
for (let i = 0; i < rules[key].length; i++) { // 遍历每一个字段的校验规则
let ruleName = rules[key][i].rule; // 获取每一个校验规则的规则名
let value = rules[key][i].value; // 获取每一个校验规则的校验值
if (!Array.isArray(value)) { // 统一校验值为数组类型
value = new Array(value)
}
let result = _this.rules[ruleName].apply(this, value); // 调用校验规则方法进行校验
if (!result) { // 如果校验不通过,就获取校验结果信息,并立即跳出循环不再执行,节约消耗
valid = {
errValue: key,
errMsg: rules[key][i].message
}
break;
}
}
if (valid) { // 如果有了校验结果,代表存在不通过的字段,则立即停止循环,节约消耗
break;
}
}
return valid; // 把校验结果反悔出去
}
formData.onsubmit = function () {
event.preventDefault()
let validator = new Validate();
let result = validator.test({
'username': [{ rule: 'isRequired', value: this.username.value, message: '用户名不能为空!' }],
'password1': [
{ rule: 'isRequired', value: this.password1.value, message: '密码不能为空!' },
{ rule: 'minLength', value: [this.password1.value, 6], message: '密码长度不能小于6个字符!' }
],
'password2': [
{ rule: 'isRequired', value: this.password2.value, message: '确认密码不能为空!' },
{ rule: 'minLength', value: [this.password2.value, 6], message: '确认密码长度不能小于6个字符!' },
{ rule: 'isEqual', value: [this.password2.value, this.password1.value], message: '确认密码与原密码不相同!' }
],
'isMobile': [
{ rule: 'isRequired', value: this.phone.value, message: '手机号不能为空!' },
{ rule: 'isMobile', value: this.phone.value, message: '手机号格式不正确!' }
]
})
if (result) {
console.log(result);
} else {
console.log('校验通过');
}
}
</script>
</body>
</html>
下次我们增加其他的字段也只是增加规则而已,而不会去修改判断的业务逻辑。
策略模式可看作为if/else判断的另一种表现形式,在达到相同目的的同时,极大的减少了代码量以及代码维护成本
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
您将如何构建一个简单的Sinatra应用程序?我正在制作,我希望该应用具有以下功能:“应用程序”更像是一个包含所有信息的管理仪表板。然后另一个应用程序将通过REST访问信息。我还没有创建仪表板,只是从数据库中获取东西session和身份验证(尚未实现)您可以上传图片,其他应用可以显示这些图片我已经使用RSpec创建了一个测试文件通过Prawn生成报告目前的设置是这样的:app.rbtest_app.rb因为我实际上只有应用程序和测试文件。到目前为止,我已经将Datamapper用于ORM,将SQLite用于数据库。这是我的第一个Ruby/Sinatra项目,所以欢迎任何和所有建议-我应
了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl
我想编写一个ruby脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"
我经常迷上ruby的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情
这应该是一个简单的问题,但我找不到任何相关信息。给定一个Ruby中的正则表达式,对于每个匹配项,我需要检索匹配的模式$1、$2,但我还需要匹配位置。我知道=~运算符为我提供了第一个匹配项的位置,而string.scan(/regex/)为我提供了所有匹配模式。如果可能,我需要在同一步骤中获得两个结果。 最佳答案 MatchDatastring.scan(regex)do$1#Patternatfirstposition$2#Patternatsecondposition$~.offset(1)#Startingandendingpo