JavaScript戦略モデル

11268 ワード

戦略パターンの定義:一連のアルゴリズムを定義し、それらの意味をカプセル化し、相互に置き換えることができる.
ポリシーモードでボーナスを計算します.
ボーナスの支給シーン:パフォーマンスがSの人の年末賞は4倍の給料で、成績はAの年末賞は3倍の給料で、成績はBの年末賞は2倍の給料で、それでは私達のプログラミングのコードです.
最初のコードの実装
var calculateBouns = function(performanceLevel, salary){

  if(performanceLevel === 'S'){
    return salary * 4;
  }

  if(performanceLevel === 'A'){
    return salary*3;
  }

  if(performanceLevel === 'B'){
    return salary*2;
  }
};

calculateBouns ('B', 2000); // 4000
calculateBouns ('S', 6000); // 24000
今のこのコードはとても簡単ですが、分かりやすい問題がたくさんあります.
  • に多くのレベルがあると、if-elseの文が多くなり、このような文は論理分岐全体をカバーする必要がある.
  • この関数は弾力性に欠けています.Cを一つ追加すると、Sの係数を変更すると、関数の内部に入って実現しなければなりません.オープン・クローズの原則に違反します.
  • アルゴリズムの多重性が悪く、他のところで重複した論理を行うと、コピー貼り付けしかできず、直接コードの多重化ができない.
  • 上のコードには大きな問題がありますので、コードを再構成することにしました.
    組合せ関数を使ってコードの再構成を行うと、私たちが最も容易に考えられる方法は、組合せ関数によってコードの再構成を行うことであり、様々なアルゴリズムを小さな関数にパッケージ化して、これらの小さな関数に良い名前が付けられています.どのアルゴリズムが使われているかを一目で知ることができます.上の使用シーンですか?コードの最適化を行います.
    var performanceLevelS = function(){
      return salary*4;
    }
    var performanceLevelA = function(){
      return salary*3;
    }
    var performanceLevelB = function(){
      return salary*2;
    }
    
    var calculateBouns = function(performanceLevel, salary){
    
      if(performanceLevel === 'S'){
        return performanceLevelS;
      }
    
      if(performanceLevel === 'A'){
        return performanceLevelA;
      }
    
      if(performanceLevel === 'B'){
        return performanceLevelB;
      }
    };
    
    calculateBouns ('B', 2000); // 4000
    
    上記の書き方はある程度の問題を解決しましたが、このような改善は十分に限られています.私たちの論理が複雑なとき、calculateBouns関数はますます大きくなるかもしれません.
    戦略モード再構成コードを使用して、戦略モードを使ってコードを再構成できると考えています.戦略モードの意味は、一連のアルゴリズムを定義し、これらのアルゴリズムをパッケージ化して、不変の部分と変化の部分を分割することが、各設計モードの主要問題であり、戦略モードも例外ではありません.戦略モードの目的はアルゴリズムの使用と実現を分割することである.例では、アルゴリズムの使用方法は不変であり、いずれもあるアルゴリズムに基づいて計算されたボーナスの金額であり、またアルゴリズムの実現はそれぞれ異なっていて、変化しています.異なるパフォーマンスは異なる計算式に対応しています.一つのポリシーモードに基づくプログラムは少なくとも二つの部分が必要で、第一の部分はポリシークラスである.具体的なアルゴリズムを実装し、具体的な計算を行う部分である.第二の部分は環境種類のcontextで、お客様の要求を受けて、あるポリシークラスに依頼します.したがって、contextには、あるポリシーオブジェクトに対する参照が維持される.まず策略類を使って伝統的な対象に向かう実現を模倣して、パフォーマンスの計算規則を対応する策略種類の中に入れます.
    var performanceLevelS = function(){};
    
    performanceLevelS.prototype.calculate = function(salary){
      return salary*4;
    }
    
    var performanceLevelA = function(){};
    
    performanceLevelA.prototype.calculate = function(salary){
      return salary*3;
    }
    
    var performanceLevelB = function(){};
    
    performanceLevelB.prototype.calculate = function(salary){
      return salary*2;
    }
    
    ボーナスの種類を定義します.
    var Bonus = function(){
      this.strategy = null;
      this.salary = null;
    };
    
    Bonus.prototype.setSalary = function(salary){
      this.salary = salary;
    }
    
    Bonus.prototype.setStrategy = function(strategy){
      this.strategy = strategy;
    }
    
    Bonus.prototype.getBonus = function(){
      return this.strategy.calculate(this.salary);
    }
    
    戦略モードの考えを見ています.一連のアルゴリズムを定義し、それらをパッケージ化して、お互いに交換することができます.私たちは、一連のアルゴリズムを定義し、それぞれをポリシークラスにパッケージ化し、アルゴリズムをポリシークラスの内部にパッケージ化する方法で、クライアントがcontextに対して要求を開始し、Contextは常に要求をこれらのポリシーオブジェクトの中間のいずれかに委託して計算する.残りのコードをどうやって完了するかについては、まずbonusオブジェクトを作成し、bonusオブジェクトに元のデータを設定し、その後、あるボーナス計算のポリシーオブジェクトをbonusオブジェクトの内部に入力して保存します.bonus.getBonusを呼び出した場合、bonusオブジェクト自体は計算する能力がありません.前に保存したポリシーの対象に依頼します.
    var  bonus = new Bonus();
    
    bonus.setSalary(1000); //       
    bonus.setStrategy (new performanceLevelA ()); //    
    console.log(bonus.getBonus());
    
    JavaScriptバージョンに対するポリシーモード上のコードは、従来のオブジェクト指向言語を模倣して実装されていますが、実際にはJavaScript言語では関数も対象ですので、最も簡単で直接的な方法はstrategyを関数として指定することです.
    var strategis = {
      "S":function(salary){
        return salary * 4;
      }
      "A":function(salary){
          return salary * 3;
        }
      "B":function(salary){
          return salary * 2;
        }
    };
    
    同様に、contextBonusクラスで識別しなければならない必要はありません.私たちは相変わらずcalculateBonus関数を使ってcontextとしてユーザの要求を受け入れています.
    var strategis = {
      "S":function(salary){
        return salary * 4;
      }
      "A":function(salary){
          return salary * 3;
        }
      "B":function(salary){
          return salary * 2;
        }
    };
    var calculateBonus = function(level, salary){  
    
    console.log(calculateBonus('S'),20000) // 80000
    }
    
    戦略モードを通してコードの再構成を行い,元の手順における大きな部分の条件分岐文を除去した.多くの計算に関する論理はcontextにおいてはなく、ポリシーオブジェクトに分布しており、contextは計算能力がない代わりに、この職責をあるポリシーオブジェクトに委託している.各ポリシーオブジェクトが担当するアルゴリズムは、それぞれのオブジェクトの内部にカプセル化され、従来の多形に少し向かうため、各ポリシーオブジェクトは互いに以前に交換可能である.フォームの検証は登録ページを書くと仮定します.登録ボタンをクリックする前に、いくつかのチェックロジックを追加します.
  • ユーザ名は空です.
  • です.
  • パスワードの長さは6ビット
  • を下回ってはいけません.
  • 携帯電話番号はフォーマット
  • に適合していなければなりません.
    まだ戦略モードを導入していないとき、私たちのコードは次のようになります.
    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]{9}$)/.test(registerForm.phoneNumber.value) ){
            alert('         ');
            return false;   
        }
    }
    
    これは最も簡単で一般的な符号化方式ですが、ボーナス計算の最初のテンプレートと同じ欠点があります.
  • registerForm.onsubmit関数は比較的に大きいです.特にチェックしたいテーブルが比較的責任がある場合、多くのif-else文が含まれています.これらの語句は現在の検査規則をカバーする必要があります.
  • registerForm.onsubmit関数は弾力性に欠けています.新しい検査規則を追加する場合、または6をパスワード8ビットに変更する場合、関数の内部に深く入り込んで実現しなければなりません.開閉原則に違反しました.
  • アルゴリズムの多重性が悪く、もしもう一つのフォームが検証されるなら、私たちはこの論理をほぼ完全にコピーします.
  • ポリシーモードのフォーム検証
    var strategies = {
        isNotEmpty: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]{9}$)/.test(value)){
                return errorMsg;
            }
        }
    }
    
    各アルゴリズムを簡単にパッケージ化し、後続の呼び出しに便利を提供するため、validatorクラスをcontextとして実現し、ユーザの要求を受信し、strategyオブジェクトに委託するつもりです.
    var validataFunc = function(){
        var validator = new Validator();
            
        //        
        validator.add(registerForm.userName, 'isNotEmpty', '      ');
        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(){ //  errorMsg    ,        
        if(errorMsg){
            alert(errorMsg);
            return false;//       
        }
    }
    
    コードでは、まずvalidatorオブジェクトを作成し、validator.add方法でvalidatorオブジェクトにいくつかのチェックルールを追加します.validator.add方法では、3つのパラメータを受け付けますが、以下のように説明しますか?validator.add(registerForm.password, 'minLength:6', ' 6 ');
  • registerForm.passwordは、検査に参加するinputの入力ブロック
  • である.
  • minLength:6はコロンで区切られた文字列で、コロンの前はアルゴリズムの名前で、コロンの後は私達が入るパラメータです.追加の情報が必要でなければ、コロンを追加せずにアルゴリズムの名前を直接入力してください.
  • 第3のパラメータ:チェックが通っていないときに返されるエラー情報
  • validatorオブジェクトに一連のチェックルールを追加した後、validator.start()方法を呼び出して検証を開始し、この方法が正確なerrorMsgの値を返すと、onsubmit方法はfalseに戻ってフォームの提出を阻止する.最後にValidatorクラスの実装を構築しましょう.
    var Validator = function (){
        this.cache = []; //       
    }
    Validator.prototype.add = function(dom, rule, erroMsg){
        for(var i = 0, validataFunc; validataFunc = this.cache[i++]){
            var msg = validataFunc();
            if(msg){
                return msg;
            }
        }
    }
    
    私たちはルールを追加したいです.簡単なフォーム検証を配置するだけで、これらのルールはプログラムのどこにも簡単に多重化できます.プラグインの形で他の項目に移植することもできます.パスワードの長さを変更したいなら、10桁以下ではいけません.validator.add(registerForm.password, 'minLength:10', ' 10 ')を呼び出します.テキストボックスに複数のチェックルールを追加します.テキストボックスに複数のチェックルールを追加したいなら、例えば、入力したユーザ名が空ではなく、長さが10桁以下であることを確認したいですが、どうやってチェックしますか?
    validator.add(registerForm.userName, [
    {
    strategy:'isNotEmpty',
    errorMsg:'       '
    }, {
    strategy:'minLength:10',
    errorMsg:'         10 '
    }
    ]);
    
    //    
    var strategies = {
        isNotEmpty: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]{9}$)/.test(value)){
                return errorMsg;
            }
        }
    }
    var Validator = function (){
        this.cache = []; //       
    }
    Validator.prototype.add = function(dom, rules){
        var self = this;
        for(var i = 0, rule; rule = rules[i++]){
            (function(rule){
                var strategyAry = rule.strategy.split(':');
                var errorMsg = rule.errorMsg;
                self.cache.push(function(){
                    var strategy = strategyAry.shift();
                    strategyAry.unshift(dom.value);
                    strategyAry.push(errorMsg);
                    return strategies[strategy].apply(dom, strategyAry);
                });
            })(rule)
        }
    };
    Validator.prototype.start = function(){
        for(var i = 0, validataFunc; validataFunc = this.cache[i++]){
            var msg = validataFunc();
            if(msg){
                return msg;
            }
        }
    };      
    //          
    var registerForm = document.getElementById('registerForm');
    var validataFunc = function(){
        var validator = new Validator();
                
        validator.add(registerForm.userName,[{
            strategy: 'isNotEmpty',
            errorMsg: '       '
        },
        {
            strategy: 'minLength:10',
            errorMsg: '         10'
        }]);
        validator.add(registerForm.password,[{
            strategy: 'minLength:6',
            errorMsg: '       6'
        }]);
        validator.add(registerForm.phoneNumber,[{
            strategy: 'isMobile',
            errorMsg: '         '
        }]);
        var errorMsg = validator.start();
        return errorMsg;
    }
    registerForm.onsubmit = function(){
                
        var errorMsg = validataFunc();
        if(errorMsg){
            alert(errorMsg);    
            return false;
        }
    }
    
    ポリシーモードの利点:
  • ポリシーモードは、組合せ、依頼、および多状態を利用して、多重条件選択
  • を回避することができる.
  • は、オープン・クローズ原則を完全にサポートし、アルゴリズムをstrategyに独立してパッケージ化することができ、アルゴリズム間の切り替えが容易であり、分かりやすく、拡張が容易である.
  • ポリシーモードのアルゴリズムは、システムの他の場所にも多重化され、多くの重複貼り付けを回避することができる.
  • ポリシーパターンは、組み合わせと委託Contextを利用してアルゴリズムを実行する能力を持ち、これも継承のためのより便利な代替案である.