js中的策略模式
阿新 • • 發佈:2021-07-12
策略模式
策略模式的定義:定義一系列的演算法,把它們一個個封裝起來,並使它們可以互相替換
。
簡單來說就是我要到某個地方去旅遊,到目的地的過程有很多:飛機,高鐵,汽車。。。這些方法都作為一個封裝,等我要出發時只需要選擇一個方法就可以去了,則就是策略模式。
所以策略模式的關鍵就是封裝不同的方法,然後呼叫其中一個。
以表單驗證為例:
- 該表單接收名字、郵箱、密碼三個輸入
- 名字必須輸入且不能超過四個長度
- 郵箱可選輸入但郵箱必須要含有
@
- 密碼必須輸入且長度不能少於三個
這是沒有使用策略模式的驗證:
btn.addEventListener("click", function () { if (!uname.value) { alert("要輸入名字"); return; } if (uname.value.length > 4) { alert("名字太長"); return; } if (email.value && !email.value.match(/@/g)) { alert("郵箱格式不正確"); return; } if (!pwd.value) { alert("要輸入密碼"); return; } if (pwd.value.length < 3) { alert("密碼太短"); return; } //todo 提交表單 });
可以看到如果某天我要把郵箱的驗證換成更復雜的,密碼長度改成不少於4,都會進入該函式中修改,不符合違反開放封閉原則
接下來可以用策略模式的思想把判斷的過程提取出來:
methods = { isEmpty(element, errMsg) { element.value ?? alert(errMsg); }, isLonger(element, length, errMsg) { element.value.length >= length || alert(errMsg); }, isShorter(element, length, errMsg) { element.value.length < length || alert(errMsg); }, isEmail(element, regexp, errMsg) { element.value.match(regexp) || alert(errMsg); }, };
現在把每個判斷都分拆為一個物件方法,接下來只要把方法對應到要判斷的元素上,因為一個元素可能會同時運用多個策略,所以還能用一個包裝類來收集:
methods = { notEmpty(element, errMsg) { return element.value || (alert(errMsg), "error"); }, canEmpty(element) { return element.value ? true : "break"; }, noLonger(element, length, errMsg) { return element.value.length < length || (alert(errMsg), "error"); }, noShorter(element, length, errMsg) { return element.value.length > length || (alert(errMsg), "error"); }, isEmail(element, regexp, errMsg) { return element.value.match(regexp) || (alert(errMsg), "error"); }, }; class Check { constructor() { this.element = new Map(); //? 用來收集每個元素應用的策略 } put(element, methodName, ...arg) { if (this.element.has(element)) { this.element.get(element).push({ methodName, args: arg }); } else { this.element.set(element, [{ methodName, args: arg }]); } } start() { //? 迭代收集到的資料 for (const obj of this.element) { check: { for (const aaa of obj[1]) { //? 如果有錯誤會返回false,然後跳出現在的迴圈 switch (methods[aaa.methodName](obj[0], ...aaa.args)) { case "break": break check; case "error": return false; } } } } return true; } } const checkInput = new Check(); checkInput.put(uname, "notEmpty", "名字不能為空"); checkInput.put(uname, "noLonger", 4, "名字太長"); checkInput.put(email, "canEmpty"); checkInput.put(email, "isEmail", /@/g, "郵箱不正確"); checkInput.put(pwd, "notEmpty", "密碼不能為空"); checkInput.put(pwd, "noShorter", 3, "密碼太短"); btn.addEventListener("click", function () { if (checkInput.start()) { //todo 提交 } });
現在雖然整個程式碼執行過程變得更復雜,但是在實現時不需要關注程式碼邏輯,直接新增現有的方法即可,每個方法都有可複用性,同時在method
上也能新增或修改策略方法。