javascript设计模式之二——策略模式

策略模式

策略模式:定义一些算法,把它们一个个封装起来,使它们可以互相替换。此模式让算法的变化不会影响到使用算法的客户。
应用场景:在设计程序中,当我们实现某种功能的时候,他有很多种选择,这些算法灵活多样,这时候我们需要用到策略模式,将每个算法封装起来,可以交替使用。实践中,不仅可以封装算法,也可以用来封装几乎任何类型的规则,是要在分析过程中需要在不同时间应用不同的业务规则,就可以考虑是要策略模式来处理各种变化。
策略模式的程序至少包含两个部分:第一部分是一组策略类,策略类封装了具体的算法,负责具体的计算过程;第二部分是环境类,环境类是接受用户的请求,随后把请求委托给某个策略类。
实现代码:
实例场景:在web项目中,我们常常会遇到注册、登录、修改等功能的实现,这都离不开表单的提交,在提交表单时,我们需要验证数据的合理性。如果用if-else语句来判断,这些语句需要覆盖所有的逻辑分支,同时,如果要增加新的条件,必须到这段代码内部进行修改,违反了开放-封闭原则,并且代码的复用性很差,这时,我们需要使用策略模式。
下面以表单提交为例,来理解策略模式的思想。
在不用策略模式的情况下,我们可能会这样处理:
代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
        <form action="http:// xxx.com/register" id="registerForm" method="post">
    请输入用户名:<input type="text" name="userName"/>
    请输入密码:<input type="text" name="password"/>
    请输入手机号码:<input type="text" name="phoneNumber"/>
    <button>提交</button>
</form>
<script>
    var registerForm = document.getElementById( 'registerForm' );
    registerForm.onsubmit = function(){
        if ( registerForm.userName.value === '' ){
            alert ( '用户名不能为空' );
            return false;
        }
        if ( registerForm.password.value.length < 6 ){
            alert ( '密码长度不能少于6位' );
            return false;
        }
        if ( !/(^1[3|5|8][0-9]{9}$)/.test( registerForm.phoneNumber.value ) ){
            alert ( '手机号码格式不正确' );
            return false;
        }
    }
</script>
</body>
</html>

上述代码虽然实现了最基本的功能,但是存在的问题如下:
(1)registerForm.onsubmit函数中包含大量的if-else语句,必须包含所有的校验规则,这是函数的代码量比较庞大;
(2)registerForm.onsubmit函数缺乏弹性,如果要增加新的规则或修改某些规则,必须对该函数进行修改,不利于代码的维护,违反了开放-封闭规则;
(3)代码的可复用性较差,当其他地方也需要使用该校验规则时,必须复制该代码,导致代码混乱。
为了避免以上问题,我们用策略模式进行设计,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>

<form action="http:// xxx.com/register" id="registerForm" method="post">
    请输入用户名:<input type="text" name="userName"/>
    请输入密码:<input type="text" name="password"/>
    请输入手机号码:<input type="text" name="phoneNumber"/>
    <button>提交</button>
</form>
<script>
    //step1:将校验逻辑封装成策略对象,包含所有需要校验的信息
    var strategies = {
        isNonEmpty: function (value, errorMsg) {//输入不为空
            if (value === '') {
                return errorMsg;
            }
        },

        minLength: function (value, length, errorMsg) {//测试输入最小长度
            if (value.length < length) {
                return errorMsg;
            }
        },

        isMobile: function (value, errorMsg) { //手机号码格式
            if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
                return errorMsg;
            }
        }

    };

    // step2: Validator类作为环境类,接受用户的请求,用户的校验信息,随后把请求委托给strategies类
    var validataFunc = function () {
        var validator = new Validator();    // 创建一个validator对象
        /****************添加一些校验规则****************/
        validator.add(registerForm.userName, 'isNonEmpty', '用户名不能为空');
        validator.add(registerForm.password, 'minLength:6', '密码长度不能少于6位');
        validator.add(registerForm.phoneNumber, 'isMobile', '手机号码格式不正确');
        var errorMsg = validator.start(); // 获得校验结果
        return errorMsg; // 返回校验结果
    };
    var Validator = function () {
        this.cache = []; // 保存校验规则
    };
    Validator.prototype.add = function (dom, rule, errorMsg) {
        var ary = rule.split(':'); // 把strategy和参数分开
        this.cache.push(function () { // 把校验的步骤用空函数包装起来,并且放入cache
            var strategy = ary.shift(); // 用户挑选的strategy
            ary.unshift(dom.value); // 把input的value添加进参数列表
            ary.push(errorMsg); // 把errorMsg添加进参数列表
            return strategies[strategy].apply(dom, ary);
        });
    };
    Validator.prototype.start = function () {
        for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
            var msg = validatorFunc(); // 开始校验,并取得校验后的返回信息
            if (msg) { // 如果有确切的返回值,说明校验没有通过
                return msg;
            }
        }
    };

    var registerForm = document.getElementById('registerForm');
    registerForm.onsubmit = function () {
        var errorMsg = validataFunc(); // 如果errorMsg有确切的返回值,说明未通过校验
        if (errorMsg) {
            alert(errorMsg);
            return false; // 阻止表单提交
        }
    };
</script>
</body>
</html>

使用策略模式后,我们不需要使用if-else语句,仅仅通过“配置”的方式就可以完成表单的提交;当规则要添加或者修改时,只需要修改或编写少量代码即可完成,扩展性好;并且,代码有很好的复用性。
策略模式的优缺点:
优点:
1、策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
2、策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的strategy中,使得它们易于切换,易于理解,易于扩展。
3、策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
4、在策略模式中利用组合和委托来让Context拥有执行算法的能力,这也是继承的一种更轻便的替代方案。
缺点:
1、使用策略模式会在程序中增加许多策略类或者策略对象,但实际上这比把它们负责的逻辑堆砌在Context中要好。
2、要使用策略模式,必须了解所有的strategy,必须了解各个strategy之间的不
同点,这样才能选择一个合适的strategy。比如,我们要选择一种合适的旅游出行路
线,必须先了解选择飞机、火车、自行车等方案的细节。此时strategy要向客户暴露
它的所有实现,这是违反最少知识原则的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章