在我最近的一個項目中,有一個模塊是關於表單校驗的需求,需求大致是驗證輸入不能空,不能夠重複的字符串等等,檢驗的規則大概有10種,而我的同事爲了檢驗這樣的表單功能,則編寫大量的if-else代碼,僞代碼如下:
if (value.length ===0) {
// todo
} else if (value.length < 6) {
//todo
} else if (.....) {
//todo
}
可以發現上面的代碼十分簡單,也非常實用,但是也存在明顯的缺點:
- 包含了大量的if-else語句,這些語句覆蓋了大量的邏輯。
- 上述代碼缺乏彈性,如果增加了新的需求,則必須重複的添加else if。
- 邏輯的複用性很差,我如果是其它的模塊也操作相似的邏輯,則必須全部都複製一遍過去,造成一種很臃腫和很噁心的代碼。
爲了解決以上存在的問題,我們就很自然聯想到設計模式中的一種-策略模式:
策略模式的定義:
定義一系列的算法,把他們一個個封裝起來,並且使它們可以相互替換。
又比如我們現在有如下的場景:
如果績效爲S的人,那麼他的年終獎則是他工資的4倍,如果績效爲A的人,那麼他的年終獎則是他工資的3倍,如果績效爲B的人,那麼他的年終獎則是他工資的2倍。
那麼我們就自然的想到編寫以下的代碼:
let calculateBonus = (level,salary)=>{
if (level === "S") {
return salary*4
}else if (level === "A") {
return salary*3
}else if (level === "B"){
return salary * 2
}
}
console.log(calculateBonus("A",200)) // 800
console.log(calculateBonus("B",200)) // 600
那麼其實這種代碼,在某種程度上來,看着是很蠢的,也不符合,現在前端高大上,逼格高的作風。所以我們需要把它給改造一下:
let strategies = {
"S": function (salary) {
return salary * 4
},
"A": function (salary) {
return salary * 3
},
"B": function (salary) {
return salary * 2
}
}
let calcteBonus = (level, salary) => {
return strategies[level](salary);
}
console.log(calcteBonus("S", 200)) // 800
console.log(calculateBonus("B", 200)) // 600
上面的代碼,我們通過構造strategies 對象,來存放各個邏輯的函數。實際上在javascript語言中,函數也是對象,函數也可以作爲value來輸出。
通過以上的改造,我們可以看到代碼的結構更加簡潔了,else-if的語句也不存在了,瞬間感覺B格就高了很多。當然了,也不是說else-if不好,只是我們有更好實現這種需求的方式,應當選擇更爲簡潔和聰明的方式,也方便日後的維護。
改造表單驗證的代碼:
我們回到一開始的表單需求,我們放棄大量elseif的方式,從而採用更爲簡潔和聰明的形式。假設我們現在有以下的需求爲:
- 用戶名不能爲空
- 密碼長度不能少於6位
- 手機號碼必須符合格式
<body>
<form>
<p>
用戶名:<input type="text" id="username">
</p>
<p>
密碼:<input type="text" id="password">
</p>
<p>
電話:<input type="text" id="telphone">
</p>
<p>
<button id="btn" type="button">按鈕</button>
</p>
</form>
<script>
var oBtn = document.getElementById("btn");
var oUserName = document.getElementById("username");
var oPassword = document.getElementById("password");
var oTelphone = document.getElementById("telphone");
oBtn.onclick = function () {
if (oUserName.value === "") {
alert("輸入不能爲空");
return false;
}
if (oPassword.value.length < 6) {
alert("密碼不能少於6位");
return false;
}
if (!/(^1[3|5|8[0-9]{9}]$)/.test(oTelphone.value)) {
alert("手機號碼格式不正確!");
return false;
}
}
</script>
</body>
我們又自然就編寫以上的代碼,這樣我們又無法避免寫出了大量if-else的代碼。所以,我們又需要把代碼進行重構!
<body>
<form>
<p>
用戶名:<input type="text" id="username">
</p>
<p>
密碼:<input type="text" id="password">
</p>
<p>
電話:<input type="text" id="telphone">
</p>
<p>
<button id="btn" type="button">按鈕</button>
</p>
</form>
<script>
let oBtn = document.getElementById("btn");
let oUserName = document.getElementById("username");
let oPassword = document.getElementById("password");
let oTelphone = document.getElementById("telphone");
//校驗規則的對象
let strategies = {
isNoEmpty: function (value, msg) {
if (value.length === 0) {
return msg;
}
},
minLength: function (value, length, msg) {
if (value.length < length) {
return msg;
}
},
isMobile: function (value, msg) {
if (!/(^1[3|5|8[0-9]{9}]$)/.test(value)) {
return msg;
}
}
}
// 構造檢驗類
let validataFun = function () {
var validator = new Validator();
// 添加檢驗規則
validator.add(oUserName, "isNoEmpty", "用戶名不能爲空!");
validator.add(oPassword, "minLength:6", "密碼長度不能小於6!");
validator.add(oTelphone, "isMobile", "手機號碼格式不正確!");
let msg = validator.start(); // 調用開始檢驗的方法
return msg;
}
function Validator() {
this.cache = []; // 保存檢驗規則的數組
}
// 通過原型掛載add方法
Validator.prototype.add = function (dom, rule, msg) {
let ary = rule.split(":");
this.cache.push(function () { // 把檢驗的函數push進數組
let strategy = ary.shift(); // 用戶所操作的strategy,刪除第一項的數據,並返回第一項的數據
ary.unshift(dom.value); // unshift向開頭添加元素,會影響到原數組
ary.push(msg);
return strategies[strategy](...ary);
// return strategies[strategy].apply(dom, ary);
})
}
// 執行檢驗規則
Validator.prototype.start = function () {
// console.log(this.cache[0]());
for (let i = 0; i < this.cache.length; i++) {
let msg = this.cache[i]();
if (msg) {
return msg;
}
}
}
oBtn.onclick = function () {
let msg = validataFun();
if (msg) {
alert(msg); // 輸出失敗檢驗的信息
return false;
}
}
</script>
</body>
我們通過策略模式重構代碼後,我們發現可以通過配置方式來改變,某一個input的檢驗規則,例如,我們把用戶名輸入不能少於10個字符,則可以把代碼修改爲:
validator.add(oUserName, "minLength:10", "用戶名長度不能小於10!");
那麼問題來了,我希望用戶名不能爲空的同時,還希望它的長度不能少於6位,那麼上面的代碼則又需要進行修改。我們期望的是按照以下的檢驗規則來進行校驗:
validator.add(oUserName, [{
strategy: "isNoEmpty",
msg: "用戶名不能爲空!"
}, {
strategy: "minLength:6",
msg: "用戶名不能少於6位!"
}])
validator.add(oPassword, "minLength:6", "密碼長度不能小於6!");
validator.add(oTelphone, "isMobile", "手機號碼格式不正確!");
這個時候add方法,第二個參數接受的是數組,那麼我們就通過遍歷這個數組,把檢驗規則都push進cache數組中。
<body>
<form>
<p>
用戶名:<input type="text" id="username">
</p>
<p>
密碼:<input type="text" id="password">
</p>
<p>
電話:<input type="text" id="telphone">
</p>
<p>
<button id="btn" type="button">按鈕</button>
</p>
</form>
<script>
let oBtn = document.getElementById("btn");
let oUserName = document.getElementById("username");
let oPassword = document.getElementById("password");
let oTelphone = document.getElementById("telphone");
//校驗規則的對象
let strategies = {
isNoEmpty: function (value, msg) {
if (value.length === 0) {
return msg;
}
},
minLength: function (value, length, msg) {
if (value.length < length) {
return msg;
}
},
isMobile: function (value, msg) {
if (!/(^1[3|5|8[0-9]{9}]$)/.test(value)) {
return msg;
}
}
}
// 構造檢驗類
let validataFun = function () {
var validator = new Validator();
// 添加檢驗規則
// validator.add(oUserName, "isNoEmpty", "用戶名不能爲空!");
// validator.add(oPassword, "minLength:6", "密碼長度不能小於6!");
// validator.add(oTelphone, "isMobile", "手機號碼格式不正確!");
validator.add(oUserName, [{
strategy: "isNoEmpty",
msg: "用戶名不能爲空!"
}, {
strategy: "minLength:6",
msg: "用戶名不能少於6位!"
}])
validator.add(oPassword, [{
strategy: "minLength:6",
msg: "密碼不能少於6位"
}]);
validator.add(oTelphone, [{
strategy: "isMobile",
msg: "校驗規則不正確!"
}]);
let msg = validator.start(); // 調用開始檢驗的方法
return msg;
}
function Validator() {
this.cache = []; // 保存檢驗規則的數組
}
// 通過原型掛載add方法
Validator.prototype.add = function (dom, rule, msg) {
for (let i = 0; i < rule.length; i++) {
let ary = rule[i].strategy.split(":");
this.cache.push(function () { // 把檢驗的函數push進數組
let strategy = ary.shift(); // 用戶所操作的strategy,刪除第一項的數據,並返回第一項的數據
ary.unshift(dom.value); // unshift向開頭添加元素,會影響到原數組
ary.push(rule[i].msg);
return strategies[strategy](...ary);
// return strategies[strategy].apply(dom, ary);
})
}
}
// 執行檢驗規則
Validator.prototype.start = function () {
for (let i = 0; i < this.cache.length; i++) {
let msg = this.cache[i]();
console.log(msg);
if (msg) {
return msg;
}
}
}
oBtn.onclick = function () {
let msg = validataFun();
if (msg) {
alert(msg); // 輸出失敗檢驗的信息
return false;
}
}
</script>
</body>
通過策略模式,我們是可以很輕鬆去解決我們日常工作一些比較繁瑣的邏輯,當你的代碼,產生了大量的if-else語法的時候,可以嘗試使用策略模式,那麼你的代碼就變得簡潔和有B格。