JavaScriptデザインモデル読書ノート(三)=>行動型デザインモデル

42565 ワード

全シリーズカタログ
  • JavaScript設計モード読書ノート(一)=>作成型設計モード
  • JavaScript設計モード読書ノート(二)=>構造型設計モード
  • JavaScript設計モード読書ノート(三)=>行動型設計モード
  • JavaScript設計モード読書ノート(四)=>技巧型設計モード
  • JavaScript設計モード読書ノート(五)=>アーキテクチャ型設計モード、MVC、MVP、MVM
  • 記事の目次
  • 1.テンプレート方法モード
  • .観察者モード
  • 2.1.観察者を作成する
  • 2.2.オブジェクト間デカップリング
  • 2.3.ES 6 ReflectとProxyは、観察者モード
  • を実現する.
  • .状態モード
  • .ポリシーモード
  • .コマンドモード
  • .メモモード
  • 1.テンプレート方法モード
    親のセットを定義し、いくつかの実装ステップをサブクラスに延期します.
    これは自分では思っていませんでした.生産中の例をよく使います.
    // e.g.
    const Alert = function (data) {
      this.content = data.content;
      this.panel = document.createElement('div');
      this.panel.class = 'alert'
      // xxxxx
    }
    
    Alert.prototype.init = function () {
      // xxxx
    }
    
    const RightAlert = function (data) {
      Alert.call(this, data);
      this.panel.class = 'right-alert alert'
    }
    RightAlert.prototype = Object.create(Alert);
    
    ヒントボックスの実装については、次のantd Model実装方法を参照してください.これは、様々な種類のヒントボックス間のコード多重化をどのように実現するかです.
  • 細粒度モジュールは、ボトムボタン、アイコン、スタイルなどの分割を行う
  • .
  • 汎用プロンプトボックスは、事前にコンポーネントのデフォルトの設定項目を書いておく
  • 特別ヒントボックスは、1ステップの細粒モジュールによって分割され、自由にカスタマイズできる
  • .
    2.観察者モード
    これは今でも多く使われているようです.これは公開−購読者モードまたはメッセージ機構とも呼ばれ、主題オブジェクトと観察者との間の機能の結合を解決する依存関係を定義している.
    2.1.観察者を作成する
    メッセージ・コンテナと3つの方法があり、それぞれメッセージ・アプローチを購読し、購読をキャンセルし、購読メッセージを送信する方法がある.
    const Observer = (
      function() {
        const _message = {};
        return {
          //     
          regist: function(type, fn) {
            if (typeof _message[type] === 'undefined') {
              _message[type] = [fn];
            } else {
              _message[type].push(fn);
            }
          },
          //     
          fire: function(type, args = {}) {
            if (!_message[type]) return
            const events = {
              type,
              args
            }
            for (let item of _message[type]) {
              item.call(this, events);
            }
          },
          //     
          remove: function(type, fn) {
            if (Array.isArray(_message[type])) {
              _message[type] = _message[type].filter(item => item !== fn)
            }
          }
        }
      }
    )()
    
    // ==========>   
    
    Observer.regist('test', (e) => {
      console.log(e.type, e.args)
    })
    
    Observer.fire('test', 'asdasdasd'); // test, asdasdasd
    
    2.2.オブジェクト間のデカップリング
    一例を見る
    const student = function(result) {
      this.result = result;
      this.say = () =>{
        console.log(this.result);
      }
      
    }
    
    student.prototype.answer = function(question) {
      Observer.regist(question, this.say);
    }
    
    student.prototype.sleep = function(question) {
      console.log(`${this.result} ${question}     `);
      Observer.remove(question, this.say);
    }
    
    
    const teacher = function() {};
    teacher.prototype.ask = function(question) {
      console.log('    ' + question);
      Observer.fire(question);
    }
    
    const student1 = new student('1     ');
    const student2 = new student('2     ');
    
    student1.answer('    ');
    student1.answer('     ');
    student2.answer('    ');
    student2.answer('     ');
    
    student2.sleep('     ');
    
    
    const teacher1 = new teacher();
    teacher1.ask('    ');
    teacher1.ask('     ');
    
    
    
    
    
    // ================>   
    > "2                "
    > "        "
    > "1     "
    > "2     "
    > "         "
    > "1     "
    
    観察者モードの最も主要な役割は、クラスまたはオブジェクト間の結合を解決し、2つの相互依存性のあるオブジェクトを結合させ、観察者のメッセージ機構に依存させることである.
    2.3.ES 6 ReflectとProxyによる観察者モードの実現
    この小節は以下から来ていますhttp://es6.ruanyifeng.com/#docs/reflect#例:使用-Proxy-観察者モードの実現
    Proxyを使って観察者モードの一番簡単な実現を書きます.すなわち、observableとobserveの二つの関数を実現します.考え方は、Observable関数が元のオブジェクトのProxyエージェントに戻り、クリッピング動作を行い、観察者としての様々な関数をトリガする.
    const queuedObservers = new Set();
    
    const observe = fn => queuedObservers.add(fn);
    const observable = obj => new Proxy(obj, {set});
    
    function set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      queuedObservers.forEach(observer => observer());
      return result;
    }
    
    上のコードでは、まずSetセットを定義し、すべての観察者関数をこのセットに入れます.その後、observable関数は元のオブジェクトのプロキシに戻り、クリッピング動作を行う.ブロッキング関数setの中で、すべての観察者が自動的に実行されます.
    使用例を以下に示す.
    const person = observable({
      name: '  ',
      age: 20
    });
    
    function print() {
      console.log(`${person.name}, ${person.age}`)
    }
    
    observe(print);
    person.name = '  ';
    //   
    //   , 20
    
    3.状態モード
    状態モードは解決プログラムの中で肥大している分岐判定文の問題で、最終の目的は分岐判定プロセスを簡略化し、各分岐を一つの状態に変えて独立し、各状態の管理を便利にし、毎回実行する時すべての分岐を遍歴することではない.
    考え方:内部状態変数を作成し、動作ごとに対応する状態を内部にカプセル化し、最後の状態オブジェクトはインターフェースオブジェクトに戻り、内部の状態を変更したり呼び出したりすることができます.
    const State = function () {
      const _current = {};
      const states = {
        jump: function() {
          console.log('jump')
        },
        move: function() {
          console.log('move');
        }
      }
    
      const action = {
        changeState: function(...arg) {
          for (let item of arg) {
            _current[item] = true;
          }
          return this;
        },
        goes: function() {
          console.log('     ');
          for (let prop in _current) {
            states[prop] && states[prop]();
          }
          return this;
        }
      }
    
      return {
        ...action
      }
    }
    
    const a = new State();
    a.changeState('jump', 'move').goes().changeState('jump').goes()
    
    4.ポリシーモード
    戦略モードはコードから見ても状態モードと似ています.論理はちょっと違っています.状態モードでは状態の制御によって表現行為が決定されるので、状態間では通常相互に代替できないが、そうでないと異なる挙動結果が生じる.戦略モードのコアはアルゴリズムである.
    // e.g.
    
    const State = function () {
      const states = {
        phone: function(value) {
          return '       '
        },
        number: function(value) {
          return '       '
        }
      }
    
      const action = {
        check: function(type, value) {
          return states[type] ? states[type](value) : '          ';
        },
        add: function(type, fn) {
          states[type] = fn;
        }
      }
    
      return {
        ...action
      }
    }
    
    
    5.コマンドモード
    コマンドモードは、実行されるコマンドをカプセル化し、コマンドの送信者とコマンドの実行者との間の結合を解決するものである.各コマンドは実際には一つの操作です.
    const viewCommond = (function() {
      const action = {
        calculate: (value) => {
          console.log('    ,       ' + value.a);
        }
      }
      function execute(msg) {
        action[msg.command].call(action, msg.param);
      }
      return execute
    })()
    
    viewCommond({
      command: 'calculate',
      param: {
        a: 1
      }
    })
    
    6.メモモード
    最も主要な任務は既存のデータや状態をキャッシュし、将来のある瞬間に使うか返事を準備することです.
    ここは現在の記憶化関数に似ています.
    // =================================》           《JavaScript    》
    function memoize(f) {
        var cache = {};
        return function(){
            var key = arguments.length + Array.prototype.join.call(arguments, ",");
            if (key in cache) {
                return cache[key]
            }
            else return cache[key] = f.apply(this, arguments)
        }
    }
    
    // =================================》           memoize-one
    //      https://github.com/alexreardon/memoize-one/blob/master/src/index.js
    export function memoize (resultFn) {
      let lastArgs = []; //             
      let lastResult; //           
      let calledOnce: boolean = false; //      ,       false
    
      //                 
      //     `isEqual`        ,           
      const isNewArgEqualToLast = (newArg, index) => isEqual(newArg, lastArgs[index]);
    
      //               ,          
      const result = function (...newArgs) {
        if (
          calledOnce &&
          newArgs.length === lastArgs.length &&
          newArgs.every(isNewArgEqualToLast)
        ) {
          //           ,         
          return lastResult;
        }
    
        //             ,             
        calledOnce = true; //       
        lastArgs = newArgs; //       
        lastResult = resultFn.apply(this, newArgs); //       
    
        return lastResult;
      }
    
      //       
      return result;
    }
    
    参考資料:
  • 張容銘著javascript設計モード108-182ページ
  • JavaScript権威ガイド
  • https://github.com/alexreardon/memoize-one/blob/master/src/index.js
  • https://zhuanlan.zhihu.com/p/37913276