[CleanCode]3章関数


小さくしろ!

  • if/else/hileドアのブロックは1行でなければなりません.→包囲関数が小さくなります.
  • にそこで関数を呼び出させます.→ブロック内の関数のネーミングが適切であれば,コードは容易に理解できる.
  • 関数のインデントは、1~2セグメントに制限されます.
  • 一つだけやろう!

  • 抽象化レベルが一つしかないとすれば、それは一つのことしかしないことだ.
    →関数を作成する理由は、最終的に大きな概念を次の抽象レベルから複数のレベルに分けるためです.
  • 関数内で他の意味のある関数を抽出できれば、多くの仕事をしています.
  • 各関数の抽象レベルは1つです!

  • の1つのみを実装するには、関数内の抽象レベルが1つである必要があります.
  • の1つの関数に複数の抽象レベルがあると、読みにくいです.
    →根本概念+細部が混同され始めると、細部が割れた窓のように増えます.
    上から下への読み取りコード:ルール
  • 各セグメント
  • は、関数内の抽象化レベルを低下させる.
  • スイッチドア

  • スイッチドアは小さくしにくいです.
  • の1つの仕事しかできないスイッチドアを作るのは難しい.
  • switch (e.type) {
    	case COMMITSSIONED:
    		return calculateCommissionedPay(e);
    	case HOURLY:
    		return calculateHourlyPay(e);
    	case SALARIED:
    		return calculateSalariedPay(e);
    	default:
    		throw new InvalidEmployeeType(e.type);
    }
    上記のコードの問題
  • 関数長
  • 新入社員のタイプが増加すると、より長くなります.
  • 仕事は一つしかしません.SRP
  • 違反
  • の新規従業員タイプを追加する場合は、コードを変更する必要があります.OCP違反(変更時にクライアントコードを変更)
  • ドアを開閉できる箱です.多形オブジェクトのコードを生成します.
    継承関係のため、非表示にして他のコードには入りません.
    public interface EmployeeFactory {
    	public Employee makeEmployee(EmployeeRecorder r) throws InvalidEmployType;
    }
    ---------------------------------------
    public class EmployeeFactoryImpl implements EmployeeFactory {
    	public Employee makeEmployee(EmployeeRecorder r) throws InvalidEmployType{
    		// switch문
    	}
    }

    叙述的な名前を使う!

  • 注釈を書くよりも叙述的な名前を書くほうがいいです.
  • の記述的な名前は、開発者の頭の中の設計にも役立ちます.
  • 一致する名称→予知可能な関数
  • 関数パラメータ


    買収
  • は理解しがたい.
  • 0(香港なし):理想的な買収
  • 1(1行)/2(2行)
  • 3個(3項):できるだけ避ける.→有効な組み合わせでテストが負担になります.
  • 4賞(複数):使用時に特別な理由が必要
  • 輸出買収は輸入買収よりも理解しにくい.
  • <一般的な単一フォーマット>
  • 買収に疑問を提起
  • boolean fileExists("MyFile")
  • パラメータを変換して結果を返します.
  • InputStream fileOpen("MyFile")
  • イベント
  • void passwordAttemptFailedNtimes(int attempts)
  • 注意)名前とコンテキストに注意し、イベント関数であることを示します.
  • <避ける単項形式>
  • 変換関数出力パラメータを使用
  • void method(StringBuffer out) (X) → StringBuffer method(StringBuffer in) (O)
  • <買収フラグ>
    買収
  • マークは醜い.→表示関数は一度に多くのことをする=trueはA、falseはBを表す.
  • プレゼンテーション(Boolean isSuite)→RenderForSuite()とRenderForSingleTest()
  • <二項関数>
    理解は
  • 単項より時間がかかります.できるだけ単抗関数に変更します.
  • 自然な順序がなければ、危険性があります.
  • assertEquals(予想、実績):予想と実績の間の順序がないため、逆のエラーが頻繁に発生します.
  • この関数が正しい場合
  • 新点(0,0):x,y直交座標系の自然順序が存在する.
  • <3つの関数>
  • は、一定の関数よりも理解する時間を必要とする.
  • <引数オブジェクト>
  • の買収が2-3個ある場合は、それを1つの等級にすることを考慮します.
  • <パラメータリスト>
    パラメータ可変関数
  • も必要です.
  • String.format("%s worked %.2f"hours.", name, hours);
  • 共通Stringフォーマット(String format,Object...args)→実際には二項関数と言える.
  • <動詞とキーワード>
  • 関数+引数:動詞+名詞
  • writeField(name)
  • キーワード
  • の追加
  • assertEqual(expected, actual) → assertEqualExpectedAndActual(expected, actual)
  • 買収
  • の順序を覚える必要はありません.
  • 付随効果は発生しないでください。


    付随効果は嘘です.
    関数の中で一つのことをすると言っているのに、こっそり他のことをしている.
  • は、時間結合(時間結合)+順序依存(順序依存)
  • を生成する.
  • は、1つのことだけをして、関数名に付与効果を加えたほうがいいです.
  • <力率>
    パワーファクタを避ける.
    関数でステータスを変更する場合は、関数が属するオブジェクトのステータスを変更する方法を選択します.
  • appendFooter(s) = void appendFooter(StringBuffer reporter); (X)
  • report.appendFooter(); (O)*このオプションを使用します.
  • コマンドとクエリーを分けて!


    関数は、オブジェクトのステータスまたは情報を返します.どちらもできません
    // ASIS - 의도가 모호하다. set하는 함수인가? username이 unclebob인지 확인하는 함수인가?
    if(set("username", "unclebob")... 
    
    // TOBE
    if(attributeExists("username")) {
    	setAttribute("username", "unclebob");
    }

    エラーコードではなく例外を使用します。


    コマンド関数からエラーコードが返され、コマンド/クエリー分離ルールに違反します.
  • if(deletePage(page) == E_OK)
  • 式として
  • コマンドを使用します.
  • が重なるコードです.
  • エラーコードが返されると、呼び出し元は直ちにエラーコードを処理しなければならないという問題に直面します.

  • try/catch文は醜い.try/catchブロックを個別の関数として抽出することが望ましい.
    public void delete(Page page){
    	try{
    		deletePageAndAllReferences(page);
    	}
    	catch (Exception e) {
    		logError(e);
    	}
    }
    
    // try블록 별도 함수로 추출
    private void deletePageAndAllReferences(Page page) throws Exception {
    	deletePage(page);
    	registry.deleteReference(page.name); // 실제동작함수
    	configKeys.deleteKey(page.name.makeKey());
    }
    // catch블록 별도 함수로 추출
    private void logError(Exception e){
    	logger.log(e.getMessage());
    }
  • deletePageAndAllReferencesとlogErrorは、通常の動作とエラー処理動作を分離します.
  • 分かりやすく修正
  • <エラー処理もタスクです.>
  • tryで開始し、catch/finally文で終了します.後続の論理はありません.
  • < Error.JAva依存磁石(magnetic)>
  • エラーコード=エラーコードが定義されている場所を返します.
  • で定義されたファイルが変更された場合は、クラスにインポートされたすべてのファイルを再コンパイル/再配置する必要があります.
  • は、既存のコードの再利用をもたらす.
    したがって、
  • は、エラーコード
  • の代わりに異常処理を用いる.
  • Exceptionクラスから派生した新しい例外は、再コンパイル/展開することなく新しい例外クラスを追加できます.
  • 繰り返さないで!


    繰り返しは万悪の源である.
    AOPとCOPはいくつかの面で冗長性を解消する戦略である.

    構造化プログラミング


    すべての関数の入口と出口は1つしか存在しません.つまり、ドアが1つしかありません.
    関数が小さくなるとreturn、break、continueを複数回使用できますが、関数のサイズにかかわらずgoto文は使用しないほうがいいです.

    関数はどう書きますか。


    普通の作文とあまり差がない.アイデアを記録する→可読性を整理する.
  • は最初は長く、複雑+繰り返し+因数テストリストも長く、+テストケースも詳細でした.
  • 関数の作成+名前変更+重複除外+削減方法、順序変更+等級を分けて祈る.
    この過程で、テストケースはいつも合格します.
  • *FitNesse(オープンソーステストツール)