JavaScript设计模式行为型设计模式--策略模式

Posted Wendy-lxq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript设计模式行为型设计模式--策略模式相关的知识,希望对你有一定的参考价值。

       俗话说“条条大路通罗马”。在现实中,很多时候也有多种途径可以到达同一个目的地,比如,我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路。在程序设计中,我们也常常遇到类似的情况,要实现某一个功能有多种方案可以选择。比如要对一个数组进行排序,我们可以选择快速排序算法,也可以选择冒泡排序算法。

       策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户。当然这些算法灵活多样,而且可以随意相互替换。这种方案呢,就是本文将要介绍的策略模式。

      贴上策略模式的官方定义:

      策略模式指的是定义一些列的算法,把他们一个个封装起来,并且使他们可以相互替换。目的就是将算法的使用与算法的实现分离开来。

     从定义上看,策略模式就是用来封装算法的。但如果把策略模式仅仅用来封装算法,未免有点大材小用。在实际开发中,我们通常会把算法的含义扩散开来,使策略模式也可以用来封装一系列的“业务规则”。只要这些业务规则指向的目标一致,并且可以被替换使用,我们就可以使用策略模式来封装他们。

     接下来想用一个表单校验的算法进行说明这个模式。

     在一个Web项目中,注册、登录、修改用户信息等功能的实现都离不开提交表单。

     假设我们正在编写一个注册的页面,在点击注册之前,有如下几条校验逻辑。

  • 用户名不能为空;
  • 密码长度不能少于6位;
  • 手机号码必须符合格式。
1、表单校验的第一个版本
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>register</title>
</head>
<body>
<form id="registerForm" method="post" action="http://###.com/register">
    <label for="userName">请输入用户名:</label>
    <input type="text" name="userName" id="userName">

    <label for="pwd">请输入密码:</label>
    <input type="text" name="password" id="pwd">

    <label for="phoneNumber">请输入密码:</label>
    <input type="text" name="phoneNumber" id="phoneNumber">

    <input type="button" value="提交">
</form>

<script>
    var registerForm = document.getElementById("registerForm");

    registerForm.onsubmit = function()
        if( registerForm.userName.value === '')
            alert("用户名不能为空!");
            return false;
        
        if( registerForm.pwd.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语句,这些语句需要覆盖所有的校验规则;       2) registerForm.onsubmit 函数缺乏弹性,如果增加了一个新的校验规则,或者想把密码的长度校验从6变成8,我们都必须深入这个函数的内部实现,这违反了开放-封闭原则;       3)算法的复用性差,如果程序中增加另外一个表单,比如登录,这个表单也需要进行一些类似的校验,那么我们很可能只是复制一下这段代码,然后相对应的进行修改,造成的后果就是相似的代码到处都有,不利于代码的维护与美观。
     那么,为了解决以上出现的这些问题,我们可以采用策略模式重构这个表单校验。 2、用策略模式重构表单校验      在重构代码的第一步就是将这些校验进行封装成策略对象。
    //利用策略模式
    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;
            
        
    ;
       接下来,我们需要实现Validator类,负责接收用户的请求并委托给strategy对象。在实现Validator类的代码之前,我们先了解下用户是如何向Validator类发送请求的。
    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 registerForm = document.getElementById("registerForm");
    registerForm.onsubmit = function ()
        var errorMsg = validataFunc();
        if(errorMsg)
            alert(errorMsg);
            return false;  //阻止表单提交
        
    ;
解析:
  1. 我们首先创建了一个 Validator对象,然后通过validator.add()方法往对象中添加一些校验规则,这个方法接受3个参数。
  2. validator.add( registerForm.userName, 'isNonEmpty', '用户名不能为空'); 其中registerForm.userName为参与校验的input输入框; 'isNonEmpty'为名字的校验参数;'用户名不能为空'是当校验未通过时返回的错误信息。
最后一部分是Validator类的实现:
    var Validator = function () 
        this.cache = [];
    ;
    Validator.prototype.add = function (dom, rule, errorMsg) 
        var ary = rule.split(':');
        this.cache.push(function () 
            var strategy = ary.shift();
            ary.unshift(dom.value);
            ary.push(errorMsg);
            return strategies[strategy].apply(dom,ary);
        );
    ;
    Validator.prototype.start =  function ()
        for( var i = 0,validataFunc;validataFunc = this.cache[ i++]; )
            var msg = validataFunc();
            if(msg)
                return msg;
            
        
    ;

     使用策略模式重构代码之后,我们仅仅通过“配置”的方式就可以完成一个表达的校验,而且这些校验规则也可以复用在程序中的其他需要校验的地方。在修改某个校验规则的时候,只需要编写或者该写少量的代码。 3、总结       策略模式定义了一系列算法,从概念上来说,所有的这些算法都是做相同的事情,只是实现不同,他可以以相同的方式调用所有的方法,减少了各种算法类与使用算法类之间的耦合。
      从另外一个层面上来说,单独定义算法类,也方便了单元测试,因为可以通过自己的算法进行单独测试。
      实践中,不仅可以封装算法,也可以用来封装几乎任何类型的规则,是要在分析过程中需要在不同时间应用不同的业务规则,就可以考虑是要策略模式来处理各种变化。
优点:
  • 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句;
  • 策略模式提供了对开放-封闭原则的完美支持,将算法进行封装,使得它们易于切换,易于理解和易于扩展;
  • 策略模式的代码可以复用在代码的其他地方,从而避免许多重复的复制黏贴工作,也使得代码更简洁。





以上是关于JavaScript设计模式行为型设计模式--策略模式的主要内容,如果未能解决你的问题,请参考以下文章

JAVA SCRIPT设计模式--行为型--设计模式之Strategy策略模式(21)

Javascript设计模式

手撸golang 行为型设计模式 策略模式

设计模式——行为型模式之策略模式

行为型设计模式 - 策略模式详解

行为型设计模式 - 策略模式详解