js設計モード--策略モード


前言
このシリーズの文章は主に「JavaScript設計モードと開発実践」によって整理されています.皆さんのためになりたいです.
文章シリーズ
js設計モード--単一例モード
js設計モード--策略モード
jsデザインモード--プロキシモード
概念
ポリシーモードの定義は、一連のアルゴリズムを定義し、それらを一つずつカプセル化し、相互に置き換えることができるようにすることである.
戦略モードとは、一連のアルゴリズムを定義し、それらを一つずつカプセル化することをいう.不変の部分と変化の部分を分離することは各設計モードのテーマであり、戦略モードも例外ではない.戦略モードの目的はアルゴリズムの使用とアルゴリズムの実現を分離することである.
ポリシーモードに基づく1つのプログラムは少なくとも2つの部分から構成される.最初の部分はポリシークラスであり、具体的なアルゴリズムをパッケージ化し、具体的な計算プロセスを担当する.第二の部分は環境種類のContectで、Contectは取引先の要求を受け入れて、その後要求をある策略種類に委託します.これを行うには、Conteextの中であるポリシーの対象に対する引用を維持することを説明します.
戦略モデルの実現は複雑ではなく、どのようにして戦略モデルの実現の背後からパッケージの変化、委託、多形性などの思想の価値を見出すかがカギとなる.
シーン
定義から見れば、ポリシーモードはアルゴリズムをカプセル化するために用いられる.しかし、戦略モデルをパッケージ化アルゴリズムだけに使うなら、役不足になります.実際の開発では、アルゴリズムの意味を拡散させ、一連の「業務規則」をパッケージ化するために戦略パターンを使用することができる.これらのトラヒック規則が指す目標が一致し、代替的に使用される限り、それらをポリシーモードでカプセル化することができる.
長所と短所
長所
  • ポリシーモードは、結合、委託、多状態などの技術と思想を利用して、複数の条件選択文を効果的に回避することができる.
  • ポリシーモードは、オープン・クローズ・原則に対する完全なサポートを提供し、独立したstrateにアルゴリズムをカプセル化し、それらをスイッチングしやすく、理解しやすく、拡張しやすいようにする.
  • ポリシーモードにおけるアルゴリズムは、システムの他の場所にも多重化され、多くのコピー・ペースト動作を回避することができる.
  • は、ポリシーモードにおいて、組み合わせと委託を利用して、Contectにアルゴリズムを実行する能力を持たせている.これも継承のためのより軽便な代替案である.
  • 欠点
  • は多くの戦略類あるいは策略の対象を増加して、しかし実際にはこれは彼らの責任を負うロジックをContectの中で積み重ねるより良いです.
  • 戦略モードを使用するには、すべてのstrate gyを理解しなければならず、それぞれのstrate gyの違いを知る必要があります.このようにして、適切なstrate gyを選択することができます.
  • しかし、これらの欠点は深刻ではない.

    ボーナスを計算する
    粗い実現
        var calculateBonus = function( performanceLevel, salary ){
            if ( performanceLevel === 'S' ){
                return salary * 4;
            }
            if ( performanceLevel === 'A' ){
                return salary * 3;
            }
            if ( performanceLevel === 'B' ){
                return salary * 2;
            }
        };
    
        calculateBonus( 'B', 20000 ); //   :40000
        calculateBonus( 'S', 6000 ); //   :24000
    
    短所:
  • caculateBonus関数は比較的に巨大で、多くのif-else文を含んでいます.
  • calculateBonus関数は弾力性に欠けています.新しいパフォーマンス等級Cを追加したら、或いはパフォーマンスSのボーナス係数を5に変更したいなら、calculateBonus関数の内部実現に深く入り込んでいかなければなりません.これは開放の原則に違反しています.
  • アルゴリズムの多重差
  • コンビネーション関数を使ってコードを再構築します.
    
        var performanceS = function( salary ){
            return salary * 4;
        };
        var performanceA = function( salary ){
            return salary * 3;
        };
        var performanceB = function( salary ){
            return salary * 2;
        };
        var calculateBonus = function( performanceLevel, salary ){
            if ( performanceLevel === 'S' ){
                return performanceS( salary );
            }
            if ( performanceLevel === 'A' ){
                return performanceA( salary );
            }
            if ( performanceLevel === 'B' ){
                return performanceB( salary );
            }
        };
        calculateBonus( 'A' , 10000 ); //   :30000
    問題は依然として存在しています.caculateBonus関数はますます巨大になる可能性があります.また、システムが変化する時に弾力性が不足します.
    ポリシーモードを使ってコードを再構築します.
    
        var performanceS = function(){};
        performanceS.prototype.calculate = function( salary ){
            return salary * 4;
        };
        var performanceA = function(){};
        performanceA.prototype.calculate = function( salary ){
            return salary * 3;
        };
        var performanceB = function(){};
        performanceB.prototype.calculate = function( salary ){
            return salary * 2;
        };
    
        //        Bonus:
    
        var Bonus = function(){
            this.salary = null; //     
            this.strategy = 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 ); //                   
        };
    
        var bonus = new Bonus();
        bonus.setSalary( 10000 );
    
        bonus.setStrategy( new performanceS() ); //       
        console.log( bonus.getBonus() ); //   :40000
        bonus.setStrategy( new performanceA() ); //       
        console.log( bonus.getBonus() ); //   :30000
    しかし、このコードは伝統的な対象言語の模倣に基づいています.以下はJavaScriptで実現する戦略モデルです.
    JavaScriptバージョンのポリシーモデル
    JavaScript言語では関数も対象ですので、もっと簡単で直接的なやり方はstrateを直接関数として定義します.
        var strategies = {
            "S": function( salary ){
                return salary * 4;
            },
            "A": function( salary ){
                return salary * 3;
            },
            "B": function( salary ){
                return salary * 2;
    
            }
        };
        var calculateBonus = function( level, salary ){
            return strategies[ level ]( salary );
        };
    
        console.log( calculateBonus( 'S', 20000 ) ); //   :80000
        console.log( calculateBonus( 'A', 10000 ) ); //   :30000
    
    クラス実現
    
    var performanceS = function () {};
    performanceS.prototype.calculate = function (salary) {
      return salary * 4;
    };
    var performanceA = function () {};
    performanceA.prototype.calculate = function (salary) {
      return salary * 3;
    };
    var performanceB = function () {};
    performanceB.prototype.calculate = function (salary) {
      return salary * 2;
    };
    
    //        Bonus:
    class Bonus {
      constructor() {
        this.salary = null; //     
      this.strategy = null; //            
      }
      setSalary(salary) {
        this.salary = salary; //          
      }
      setStrategy(strategy) {
        this.strategy = strategy; //                
      }
      getBonus() { //       
        return this.strategy.calculate(this.salary); //                   
      }
    }
    
    var bonus = new Bonus();
    bonus.setSalary(10000);
    
    bonus.setStrategy(new performanceS()); //       
    console.log(bonus.getBonus()); //   :40000
    bonus.setStrategy(new performanceA()); //       
    console.log(bonus.getBonus()); //   :30000
    アニメを見る
    目標:アニメーション類といくつかの緩動アルゴリズムを作成して、ボールを様々な緩動効果でページの中で運動させます.
    分析:
    まずアルゴリズムを緩めるのは、ボールをどのように動かすかを実現することです.
    そしてアニメーション類(つまりcontext)の役割は、以下の通りです.
  • アニメーションオブジェクトを初期化するには、動きが始まる前に、いくつかの有用な情報を事前に記録する必要があり、少なくとも以下の情報を含む.
  • アニメーションの開始時の正確な時間点.
  • アニメーションが開始された時、ボールがあるオリジナルの位置.
  • ボールが移動する目標位置.
  • 小球運動が持続する時間.
  • 小さいボールのある時間の位置を計算します.
  • ボールの位置を更新します.
  • 実装:
    
    
    
    
    
      
      
      
      Document
    
    
    
      
    div
    var tween = { linear: function (t, b, c, d) { return c * t / d + b; }, easeIn: function (t, b, c, d) { return c * (t /= d) * t + b; }, strongEaseIn: function (t, b, c, d) { return c * (t /= d) * t * t * t * t + b; }, strongEaseOut: function (t, b, c, d) { return c * ((t = t / d - 1) * t * t * t * t + 1) + b; }, sineaseIn: function (t, b, c, d) { return c * (t /= d) * t * t + b; }, sineaseOut: function (t, b, c, d) { return c * ((t = t / d - 1) * t * t + 1) + b; } }; var Animate = function (dom) { this.dom = dom; // dom this.startTime = 0; // this.startPos = 0; // ,dom , dom this.endPos = 0; // ,dom , dom this.propertyName = null; // dom css this.easing = null; // this.duration = null; // }; Animate.prototype.start = function (propertyName, endPos, duration, easing) { this.startTime = +new Date; // this.startPos = this.dom.getBoundingClientRect()[propertyName]; // dom this.propertyName = propertyName; // dom CSS this.endPos = endPos; // dom this.duration = duration; // this.easing = tween[easing]; // var self = this; var timeId = setInterval(function () { // , if (self.step() === false) { // , clearInterval(timeId); } }, 16); }; Animate.prototype.step = function () { var t = +new Date; // if (t >= this.startTime + this.duration) { // (1) this.update(this.endPos); // CSS return false; } var pos = this.easing(t - this.startTime, this.startPos, this.endPos - this.startPos, this.duration); // pos this.update(pos); // CSS }; Animate.prototype.update = function (pos) { this.dom.style[this.propertyName] = pos + 'px'; }; var div = document.getElementById('div'); var animate = new Animate(div); animate.start('left', 500, 1000, 'linear'); // animate.start( 'top', 1500, 500, 'strongEaseIn' );
    フォームを検証
    簡単な実現
    
    
    
    
      
    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; } }
    ポリシーモードを使って改善する
    
    
    
    
      
    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; } } }; 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(); // errorMsg , if (errorMsg) { alert(errorMsg); return false; // } }; 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 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 **************************/ 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, validatorFunc; validatorFunc = this.cache[i++];) { var errorMsg = validatorFunc(); if (errorMsg) { return errorMsg; } } }; /*********************** **************************/ var registerForm = document.getElementById('registerForm'); var validataFunc = function () { var validator = new Validator(); validator.add(registerForm.userName, [{ strategy: 'isNonEmpty', errorMsg: ' ' }, { strategy: 'minLength:6', errorMsg: ' 10 ' }]); validator.add(registerForm.password, [{ strategy: 'minLength:6', errorMsg: ' 6 ' }]); var errorMsg = validator.start(); return errorMsg; } registerForm.onsubmit = function () { var errorMsg = validataFunc(); if (errorMsg) { alert(errorMsg); return false; } };