再構成改善コードの設計--方法編


再構成改善コードの設計--方法編
データを再組織する
  • :分かりにくい複雑な長い関数を解決し、コードがより明確で、維持しやすいようにする.
  • :長関数が短い関数になり、アルゴリズム構造の最適化
  • :抽出関数(Extract Method)は、クエリによって取得された値(Replace Temp With query)、最適化アルゴリズム(Substitute Algorthm)
  • を取得する.
  • AFの古いプロジェクトの中にはたくさんの行が百元を超える関数があります.メンテナンスに時間がかかります.多くの関数はExtract Method方法を使って、小さな関数に分解できます.後期に論理を増やしても、コードを多重しても理解できます.また関数が長い理由は、シーンが特に多くて関数が出てきますが、これらの処理関数は一つの入口に書いてあります.やはりデータを処理するためにReplace Temp With query方式を使いたいです.
  • 抽出関数
  • の関数は、多くの注釈を書いて理解させる必要がある場合、この関数は、小関数に分解され得るはずである.良好な関数名は、コードをより分かりやすくすることができます.
  • シーン説明
  • 
        let infoArr = Array(10).fill().map((item, index) => {
            return {
                name: 'VFrank' + index,
                age: Math.floor(Math.random() * 10 + 20)
            }
        });
    
        function printInfo() {
    
            //       
            console.log('**********************')
            console.log('*****Hello VFrank*****')
            console.log('**********************')
    
            //     name age       
            let temp = { nameArr: [], ageArr: [] };
    
            infoArr.forEach(item) {
                let { name, age } = item;
                temp.nameArr.push(name);
                temp.ageArr.push(age);
            }
    
            //       
            console.log(nameArr);
            console.log(ageArr);
        }
    
    
  • 上のprintInfo関数は、各注釈は独立した関数であっても良いです.ここでは、抽出関数のいくつかの問題を説明します.
  • の最初のブロックの注釈は、内部変数に対する参照なしに、直接的に小さな関数
  • として抽出することができる.
  • の第2のブロックの注釈は、データ処理層に属していて、単独で抜き出すことができ、戻り値形式
  • である.
  • 第3の速い注釈は、印刷ロジックが内部変数に対して参照関係があり、パラメータ形式で
  • に入る必要がある.
    修正後は以下の通りです
    
        let infoArr = Array(10).fill().map((item, index) => {
            return {
                name: 'VFrank' + index,
                age: Math.floor(Math.random() * 10 + 20)
            }
        });
    
        function printInfo() {
    
            printBanner();
            let result = getOutStand(); //    Replace Temp With query    
            printDetail(result);
    
        }
    
        //              
        function printBanner() {
            console.log('**********************')
            console.log('*****Hello VFrank*****')
            console.log('**********************')
        }
    
        //         ,
        function getOutStand() {
    
            let result = { nameArr: [], ageArr: [] };
            infoArr.forEach(item) {
                let { name, age } = item;
                result.nameArr.push(name);
                result.ageArr.push(age);
            }
            return result;
    
        }
    
        //                  
        function printDetail(result) {
            console.log(result.nameArr);
            console.log(result.ageArr);
        }
    
    
  • は、クエリによって取得される値(Replace Temp With query)
  • .
  • 関数には多くの一時変数が存在します.これらの変数はこの関数だけで使用されます.これは一つの関数が長すぎて分かりにくいです.関数に変換すると、関数の粒子サイズが増加し、後続のメンテナンスに大きな助けを与えることができます.
  • シーン
  • 
        //          ,      *   
        getPrice() {
    
            let basePrice = this.quantity * this.itemPrice;
            let discountFactor = 0;
            if (basePrice > 1000) {
                discountFactor = 0.95;
            } else {
                discountFactor = 0.98;
            }
    
            return basePrice * discountFactor;
        }
    
    
  • 最初に変更しました.basePriceを抽出しました.
  •     getPrice() {
    
            let discountFactor = 0;
            if (this.getBasePrice() > 1000) {
                discountFactor = 0.95;
            } else {
                discountFactor = 0.98;
            }
    
            return this.getBasePric() * discountFactor;
        }
    
        getBasePric() {
            return this.quantity * this.itemPrice;
        }
    
    
  • 修正したらdiscount Factorも実際に抜き出すことができます.再度下記のように修正します.
    
        getPrice() {
    
            return this.getBasePric() * this.discountFactor();
        }
    
        getBasePric() {
            return this.quantity * this.itemPrice;
        }
    
        getDisCountFactor() {
    
            if (this.getBasePrice() > 1000) {return 0.95;}
            return 0.98;
        }
    
    
    オブジェクト間の移動特性
  • :解決対象が過剰な責任を負ってぶくぶく太っているシーン(例えば、業務コードMgrがよくPanelやFormのすべきことを作った場合)
  • :オブジェクトの責任を整理し、対応する関数を移動する.(責任をどこに置くかを決める)
  • :移動方法、精製類(ExTract Class)
  • 移動方法(Move Method)
  • のクラスは、他のクラスと高度に結合された関係がある場合、各クラスがより簡単に
  • になるように、関数を移動する必要がある.
  • 原則:関数とどのオブジェクトとの交流が多いかは、このオブジェクト
  • に移動します.
    データを再組織する
  • :データを容易に処理する再構成
  • :魔数を定数に、配列をオブジェクトに変換する
  • :移動方法、精製類(ExTract Class)
  • 条件式を簡略化する
  • :いくつかの条件論理の複雑な判断を簡略化する
  • :「分岐論理」と「動作詳細」を分離する
  • :分解条件式、合成条件式(Consolidate Coditional)、ネスト式
  • の代わりに、衛文で表現されている.
    分解条件式(Decompse Coditional)
  • 複雑な条件論理は複雑度の上昇を引き起こす原因の一つであり、異なる条件分岐を作成し、異なる分岐によって異なることをするが、複雑な条件判断はなぜこのようにするのか分かりません.可読性が大幅に低下するため、複数の独立した判定関数に分解し、用途によって名前を付けて区別し、条件論理を強調する必要があります.
  • シーン
  • 
        // dealWithData(num) {
        //     if (num < this.minNum || num > this.maxNum) {
        //         // doSomeThing
        //     } else {
        //         // doSomeThing
        //     }
        // }
    
    
        function dealWithData(num) {
            if (this.isOverFlow(num)) {
                // doSomeThing
            } else {
                // doSomeThing
            }
        }
    
        isOverFlow(num) {
            return num < this.minNum || num > this.maxNum
        }
    
    
    統合条件式(Consolidate Contational Expression)
  • 系統の判断条件があります.得られた結果は全部一致しました.
    
        // function dealWithData(num) {
        //     if (num < 2) {
        //         return 0;
        //     }
    
        //     if (num > this.maxNum) {
        //         return 0;
        //     }
    
        //     if (this.isFlag(num)) {
        //         return 0;
        //     }
        // }
    
        //    
        dealWithData(num) {
            if (this.isZero(num)) {
                return 0;
            }
        }
    
        isZero(num) {
            return num < 2 || num > this.maxNum || this.isFlag(num);
        }
    
    
    入れ子式を衛文で代用する(Replace Nested Coditional withGard Clauses)
  • 関数における条件論理によって、正常な実行経路が見えにくくなった場合には、衛文を使用して、
  • をクリアする.
    
    //   dealWithData() {
    //         let result;
    
    //         if (this.isManage()) {
    //             result = this.getManage();
    //         } else {
    //             if (this.isEmploy()) {
    //                 result = this.getEmploy();
    //             } else {
    //                 if (this.isReired()) {
    //                     result = this.getReired();
    //                 } else {
    //                     result = this.getDefault()
    //                 }
    //             }
    //         }
    //     }
    
        //    
        dealWithData() {
    
            if (this.isManage()) {
                return this.getManage();
            }
            if (this.isEmploy()) {
                return this.getEmploy();
            }
    
            if (this.isReired()) {
                return this.getReired();
            }
            result = this.getDefault()
        }
    
    
    単純化関数呼び出し
  • :インタフェースをより分かりやすくして、
  • を使用する.
  • :関数の戻り値、関数名、パラメータを最適化する
  • :関数改名(Rename Method)、パラメータ追加(Add Parameeter)、クエリー関数と修正関数を分離し、オブジェクトの完全性を維持する
  • .
    関数改名(Rename Method)
  • は、複雑な処理過程を小さな関数に分解し、小さな関数の名前がよくないので、関数式が何をしているのか分かりにくいです.期待される効果がありません.
    パラメータを追加(Add Parameeter)
  • のある関数は、呼び出し側からより多くの情報を必要としています.このためには、オブジェクトパラメータを追加して、このオブジェクトを関数に必要な情報に持ち込むことができます.しかし、多すぎるパラメータはデータ泥団の問題に注意しなければなりません.
  • 検索関数と修正関数を分離します.
    
        // doSomeCode(nameArr) {
        //     for (let i = 0; i < nameArr.length; i++) {
        //         if (name === 'VFrank') {
        //             showMsg();
        //             return 'VFrank'
        //         }
        //     }
        //     return ''
        // }
    
    
        //    
        doSomeCode(nameArr) {
            if (findPerson(nameArr)) {
                showMsg();
            }
        }
    
        findPerson(nameArr) {
            for (let i = 0; i < nameArr.length; i++) {
                if (name === 'JhoVFrankn') {
                    return 'VFrank'
                }
            }
        }
    
    
    概括関係を処理する
  • :継承関係を整理するには、分断関数は親またはサブクラスにあるべきですか?
  • :関数方法を継承システムと共に上下に移動する
  • .
  • :関数アップシフト(Pull Up Method)、関数ダウンシフト(Push Down Method)、テンプレート関数(Form Template Method)を作成し、親類、精製子類、精製インターフェース
  • 関数を上に移動し、関数を下に移動します.
  • いくつかの関数は、サブクラスで同じ機能を実現しています.これらの関数は親クラスから外れなければなりません.逆に、親関数のうちのある関数が特定のサブクラスにしか関係していない場合、この関数はサブクラスに移動します.
    コンストラクタ本体を上に移動
  • は、各サブクラスにいくつかの構造関数を持ち、本体はほぼ同じで、スーパークラスに関数を構築し、サブクラスに呼び出すことができる.
  • という点では、Extのfieldコンポーネント、initComponentの時にaddEvent(「focus」、「blur」)など、Extのfieldコンポーネントが多く使われています.
    テンプレート関数
  • サブクラスでは、対応するいくつかの関数は同じ順序で同様の動作を実行しますが、各関数の動作の詳細には違いがあります.これらの動作を独立した関数に置いて、同じ関数名で表現することができます.
  • Extのコンポーネントは基本的にこのようなモードで実現されています.各コンポーネントはinitComponent、onRender、afterRenderなどの関数があります.このような方式を使って、フック関数もあります.つまりライフサイクルの概念があります.
  • 再構成改善コードの設計--原則編再構成改善既存コードの設計--方法編