zoukankan      html  css  js  c++  java
  • javascript设计模式学习之五——策略模式

    一、策略模式定义:

    定义一些列的算法/规则,将它们封装起来,使得它们可以互相替换/组合使用。其目的在于将算法/规则封装起来,将算法/规则的使用与实现分离出来。

    通过策略模式,可以减少算法计算过程中大量的if-else分支,并提高复用性。

    一个策略模式的程序至少由两部分组成,一个是一组策略类,策略类封装了具体算法,并负责具体的实现过程;第二个部分是环境类context,context接受客户的请求,随后将请求委托给具体的某一个策略类。context中需要有一个变量来保存对对象的引用。

    二、java中的策略模式:

    考虑以下应用场景,大部分公司都会根据绩效发放年终奖,假设说如果绩效为A的话,年终奖发放5倍月薪;如果绩效为B的话,年终奖发放4倍月薪。按照一般的思路,计算函数中可能包含如下的分支语句,if (绩效为A) else if(绩效为B){};如果新增了一个C绩效,则需要进入计算函数的内部进行修改,因此考虑将算法的实现与算法的使用相分离。

    为了实现多态,需要定义对应的接口或者抽象类:

    package com.bobo.shejimoshi;
    
    public abstract class Performance {
     
        
        public abstract int calBonus(int salary);
        
    }

    绩效A类:

    package com.bobo.shejimoshi;
    
    public class PerformanceA extends Performance{
     
        @Override
        public int calBonus(int salary) {
            // TODO Auto-generated method stub
            return salary*4;
        }
    }

    绩效B类:

    package com.bobo.shejimoshi;
    
    public class PerformanceB extends Performance{
         
        @Override
        public int calBonus(int salary) {
            // TODO Auto-generated method stub
            return salary*5;
        }
    }

    实现context类:

    package com.bobo.shejimoshi;
    
    public class Bonus {
        private Performance perfo;
        private int salary;
        
         
        public void setPerfo(Performance perfo) {
            this.perfo = perfo;
        }
    
    
        public void setSalary(int salary) {
            this.salary = salary;
        }
    
    
        public int getBonus(int salary){
            return this.perfo.calBonus(salary);
        }
    }

    调用方法如下:

    package com.bobo.shejimoshi;
    
    public class Test {
    
        public static void main(String[] args) {
            Bonus bonus = new Bonus();
            bonus.setPerfo(new PerformanceA());
            System.out.println(bonus.getBonus(1000));//输出4000
    
            bonus.setPerfo(new PerformanceB());
            System.out.println(bonus.getBonus(1000));//输出5000
        }
    
    }

    三、javascript中的策略模式

    在javascript中,函数也是对象,因此完全不必要使用策略类,将其直接定义为函数即可。对策略的引用可以像java中通过context类的一个属性来保存其引用,可以通过函数参数来进行传递。

        //策略模式
        
        //策略实现
        var strategies={
            'S':function(salary){
                return salary*4;
            },
            'A':function(salary){
                return salary*3;
            },
            'B':function(salary){
                return salary*2;
            }
        };
            
        //context实现
        var calBonus=function(leval,salary){
            return strategies[leval].call(this,salary);
        };
    
        console.log(calBonus('S',2000));
        console.log(calBonus('A',2000));

    三、案例

    1)jquery中的动画类,其各种缓动动画,也是策略模式的具体实现;

    2)如果认为策略模式就是用来封装算法的,那未免太狭隘了,事实上,广义的算法也可以是一系列的“业务规则”,只要这些业务规则指向的目标一致,就可以被替换,甚至可以被组合使用。如下面封装的一个表单验证插件。

    //使用策略模式来实现表单验证
    var strategies = {
        'isNonEmpty': function(value, errorMsg) {
            if (value == '') {
                return errorMsg;
            }
        },
        'minLength': function(value, length, errorMsg) {
            if (value.length < length) {
                return errorMsg;
            }
        },
    };
    
    function Validate() {
        this.cache = [];
    }
    Validate.prototype.add = function(dom, rules) {
        var self = this;
        for (var i = 0; i < rules.length; i++) {
            var curRule = rules[i];
            (function(curRule) {
                var argsAry = curRule['strategy'].split(':');
                var errorMsg = curRule['errorMsg'];
                self.cache.push(function() {
                    var strategy = argsAry.shift();
                    argsAry.unshift(dom.value);
                    argsAry.push(errorMsg);
                    return strategies[strategy].apply(dom, argsAry);
                });
            })(curRule);
        }
    };
    Validate.prototype.start = function() {
        for (var i = 0; i < this.cache.length; i++) {
            var func = this.cache[i];
            var errorMsg = func();
            if (errorMsg) {
                return errorMsg;
            }
        }
    };
    var testForm = document.getElementById("testForm");
    
    function validateForm() {
        var validate = new Validate();
        validate.add(testForm.username, [{ 'strategy': 'isNonEmpty', 'errorMsg': '该字段不能为空!' }]);
        validate.add(testForm.password, [{ 'strategy': 'minLength:3', 'errorMsg': '输入长度不能小于3' }]);
        console.log(validate.cache);
        var errorMsg = validate.start();
        return errorMsg;
    }
    testForm.onsubmit = function() {
        var errorMsg = validateForm();
        if (errorMsg) {
            console.log(errorMsg);
            return false;
        }
    };

    如果采用《你不知道的javascript》中推崇的基于委托的写法,那么实现的代码如下:

    //基于委托的表单验证组件的实现
    //在编写组件之前,首先了解用户是如何使用组件的,总的来说,有两个API
    //.add(registerForm.password,[{strategy:'minLength:6',errorMsg:'长度不能小于6'},{strategy:'isNonEmpty',errorMsg:'输入不能为空'}],'长度不能小于6位');添加校验规则
    //.start()启动校验,返回错误信息
    
    var strategies={
        isNonEmpty:function(value,errorMsg){
            console.log(arguments);
            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;
            }
        }
    };
    var validator={
        //不是函数,内部不能定义变量
        //var cache=[],
        init:function(){
            this.cache=[];
        },    
        add:function(elem,rules){
            var i,len,self=this;
            for(i=0,len=rules.length;i<len;i++){
                (function(i){             
                    self.cache.push(function(){
                        //这是个闭包
                        var curRule=rules[i],
                            strategyAry=curRule.strategy.split(':'),
                            errorMsg=curRule.errorMsg,
                            strategy=strategyAry.shift();
                        strategyAry.push(errorMsg);
                        strategyAry.unshift(elem.value);
                        //console.log(strategy);
                        return strategies[strategy].apply(elem,strategyAry);    
                    });    
                })(i);
                
            }        
        },
        start:function(){
            var i,len,errorMsg;
            for(i=0,len=this.cache.length;i<len;i++){
                if((errorMsg=this.cache[i]())!==undefined){
                    return errorMsg;
                }
            }
        }
         
    };
    
    //测试
    var form=document.forms['login'];
    
    var myValid=Object.create(validator);
    myValid.init();
    myValid.add(form.name,[{strategy:'isNonEmpty',errorMsg:'字段不能为空'},{strategy:'minLength:3',errorMsg:'长度不能小于3'}]);
    console.log(myValid.start());
  • 相关阅读:
    解决:oracle+myBatis ResultMap 类型为 map 时,表字段类型有 Long/Blob/Clob 时报错
    总结:独立开发 jar 包组件——功能主要是支持查询数据库的所有表数据
    解决 iframe 后退不是主页面后退(浏览器 history)问题
    解决访问 jar 包里面的字体报错:OTS parsing error: incorrect file size in WOFF header
    html 如何访问 jar 包里面的静态资源(js、css、字体等)
    css3 实现打字机效果
    js 图形验证码
    input 设置 flex:1不起作用
    vue 样式加scoped不起作用
    node-mongoose开发中常见警告或问题-持续更新
  • 原文地址:https://www.cnblogs.com/bobodeboke/p/5614771.html
Copyright © 2011-2022 走看看