Policy pattern in JS

Time:2022-5-6

Strategy mode

Definition of policy mode:Define a series of algorithms, encapsulate them one by one, and make them replace each other

In short, I want to travel to a certain place. There are many processes to the destination: plane, high-speed rail, car… These methods are used as a package. When I want to start, I only need to choose one method, which is the strategy mode.

So the key to the policy pattern is to encapsulate different methods and then call one of them.

Take form validation as an example:

  • The form receives three inputs: name, email address and password
  • The first name must be entered and cannot exceed four lengths
  • Mailbox is optional, but the mailbox must contain@
  • The password must be entered and cannot be less than three in length

This is validation without using policy mode:

btn.addEventListener("click", function () {
    if (!uname.value) {
        Alert ("name to be entered");
        return;
    }
    if (uname.value.length > 4) {
        Alert ("the name is too long");
        return;
    }
    if (email.value && !email.value.match(/@/g)) {
        Alert ("incorrect email format");
        return;
    }
    if (!pwd.value) {
        Alert ("password to be entered");
        return;
    }
    if (pwd.value.length < 3) {
        Alert ("password too short");
        return;
    }
    //Todo submit form
});

It can be seen that if one day I want to change the verification of the mailbox to a more complex one, and the password length is changed to no less than 4, it will be modified in this function, which does not comply with the open and closed principle

Next, the judgment process can be extracted with the idea of strategy mode:

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);
    },
};

Now split each judgment into an object method. Next, just map the method to the element to be judged. Because an element may use multiple strategies at the same time, it can also be collected with a wrapper class:

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(); //?  Used to collect the policies applied by each element
    }
    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() {
        //?  Data collected by iteration
        for (const obj of this.element) {
            check: {
                for (const aaa of obj[1]) {
                    //?  If there is an error, it will return false, and then jump out of the current loop
                    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", "name cannot be empty");
checkInput. Put (uname, "nolonger", 4, "name too long");
checkInput.put(email, "canEmpty");
checkInput. Put (email, "isEmail", / @ / g, "incorrect email");
checkInput. Put (PWD, "notempty", "password cannot be empty");
checkInput. Put (PWD, "noshorter", 3, "password too short");

btn.addEventListener("click", function () {
    if (checkInput.start()) {
        //Todo submission
    }
});

Now, although the whole code execution process becomes more complex, you don’t need to pay attention to the code logic in the implementation. You can directly add existing methods. Each method has reusability, and at the same timemethodYou can also add or modify policy methods on.