[Clean Code] 3. n.関数


中に入る。


私は最近RxSWIFTを使用するプロジェクトを行い、できるだけSide Effectを生成しないように関数を設計しようと努力したことがあります.そしてコードコメントを行い、関数をどの単位に分けますか...関数名、伝達、または名前について多くの話題を議論し、クリンコードがこれに対してどのような哲学を持っているか知りたい.

1.小さくなる!


関数を作成する最初のルールと2番目のルールも「小さくなります!」はい.関数を小さくすると、1つの役割しか果たされず、より明確になります.したがって,できるだけ小さくし,ブロックが深くなったときに関数分離を行う必要がある.
関数を設計する際,再利用性を向上させるために,関数名に適合する機能のみを含め,より明確にすることをよく考えている.しかし、複雑な論理があると守りにくい.この場合、関数を過度に分裂させたくないのではなく、ゆっくりと遠くからもう一度見てから再構築すると、関数の構造がより良くなるようです.

[原句]一つだけやる!


著者によると、過去30年以上にわたって、開発者への忠告が様々に表現されてきたという.
「関数は一つのことをしなければならない.そのことをしなければならない.そのことだけをしなければならない.」
前述したように、関数は1つのロールのみを実行してこそ、明確さを確保することができます.では、最も重要なのは、どのようにして「ロール」を確定しますか?本で説明するステップの1つは、指定した関数名の下で抽象レベルを実行するステップです.
うん.わかりにくいですが、関数が「一つのこと」しかしていないかどうかを判断する方法もあります.別の表現だけでなく、意味のある名前で別の関数を抽出できる場合は、複数の操作が実行されます.

3.各関数の抽象度は1です!


関数が1つの操作のみを実行するようにするには、関数内のすべての文の抽象レベルが同じである必要があります.1つの関数に抽象レベルをブレンドすると、読者は混乱します.基本概念と詳細がブレンドされ始めると、関数に詳細が追加されます.
また,抽象レベルの高い関数については上に記述し,抽象レベルの低い関数については下に記述する.これを下降規則と呼ぶ.

4.スイッチドア


スイッチ文を小さくするのは難しいです.本質的に,switch文はN件の事柄を処理する.自然に長くなるしかない次の問題が発生します.
  • 関数が長くなります.
  • は、1つのタスクのみを実行します.
  • 単一責任原則に違反する.
  • Open Closed Principle違反.
  • 著者らはswitch文を抽象ファクトリに非表示にし,マルチフォームオブジェクトを生成するコードでのみ使用する.
    SWIFTはスイッチドアをenumタイプと一緒に簡単に使用でき、SWIFTでこの部分をどのように説明すればいいか分かりません.しかし、著者の意図に従って上記の問題が発生することに同意します.特にenumタイプのcaseを追加する場合、スイッチ文が多すぎると、変更が必要な領域が大きくなり、多くの不便をもたらします.これは分岐処理文法のようで、状況に応じて使用する必要があります.

    5.記述名を使用!


    「コードを読むときに、各インスタンスが所望の機能を実行する場合、クリーンなコードと呼ぶことができます.」こんな言葉がある
    記述的な名前を使用すると、開発者の頭の中で設計が明確になり、コードが改善されやすくなります.良い名前を選択し、コードをよりよく再構築します.
    SWIFT APIガイドでは、関数の命名方法と伝達パラメータの命名方法について説明すると、「記事のように読まれていることを確認します.」このような内容は本当に強調されています異なる場合がありますが、伝達パラメータとして前置詞を使用すると、この方法の機能をよりよく表現できます.

    6.関数パラメータ


    関数の理想パラメータ数は0です.次は1つ次は2つできるだけ3つは避けたほうがいいです.4つ以上には特別な原因が必要です.
  • 因数が多ければ多いほど、関数を理解するのに要する時間が長くなります.
  • テストの観点から見ると、買収はもっと難しい.これは、関数の様々なパラメータの組合せを検証するためにテストケースを作成する必要があるためです.
  • UImitで使用されている関数だけを見ると、パラメータは非常に多いです.4.5以上の関数が見られましたパラメータが多ければ多いほどテストの作成が難しくなることに同意しますが、非同期タスクが多い関数式プログラミングモードの利点を持つiOSでは、純粋な関数を作成するのは難しいと思います.

    7.付随効果を生み出さない!


    多くの場合、これは時間的なマージ(時間結合)または順序依存(order dependency)をもたらす.これは、メソッド自体に問題はないように見えますが、メソッドが他のメソッドまたはオブジェクトと時間的な関連付けまたは順序を持っている場合、元のメソッドの目的以外のコンテンツに影響を及ぼす可能性があることを意味します.最も一般的な場合、ある方法が特定のcacheをクリアする機能を含む場合、その方法自体は問題ないが、順序の問題のため、他の方法は意外にも空のcacheを受信し続ける可能性がある.

    8.コマンドとクエリーの分離!


    関数は、操作を実行したり、質問に答えたり、両方を兼ねている必要があります.どちらもできません両者を兼ねると混乱を招く.set(attribute: String, value: String) -> Boolという方法があれば
    if (set(attribute: "username", value: "unclebob")
    上に示すように.本当に気まずいこの関数が設定されているか確認されているかを混同する可能性があります.このような混乱を避けるためには、次のようにコマンドとクエリーを分離する必要があります.
    if attributeExists(attribute: "username") {
    	setAttribute(attribute: "username", value: "unclebob")
    }

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


    コマンド関数がエラーコードを返す方法は、コマンド/クエリー分離規則に微妙に違反しています.これはif文でコマンドを式として簡単に使用できるためです.
    エラーコードではなく例外を使用すると、エラー処理コードが元のコードから分離され、コードがよりきれいになります.
    try {
    	deletePage(page)
        registry.deleteReference(page.name)
        configKeys.deleteKey(page.name.makeKey)
    } catch {
    	logger.log(e.gotMessage())
    }
    「try/catchブロックはもともと醜い」
    正常動作とエラー処理動作を混在させる.したがって、try/catchブロックを個別の関数として抽出することが望ましい.
    次のように分けたほうがいいです
    func delete(page: Page) {
    	try {
        	deletePageAndAllReferences(page: page)
        } catch {
        	logError(error)
        }
    }
    
    private func deletePageAndAllReferences(page: Page) throws {
    	deletePage(with: page)
        registry.deleteReference(page.name)
        configKeys.deleteKey(page.name.makeKey())
    }
    
    private func logError(_ error: Error) {
    	logger.log(error.description)
    }
    関数は「一つのことしかできない」というルールを守っていると見なすことができます.

    10.繰り返さない


    コード長の増加と論理の変化に伴い、1つの場所ではなく複数の場所を変更する必要があります.そのため、メンテナンス性が悪く、エラーが発生する確率はn倍です.

    関数はどう書きますか。


    プログラミングは書くことにほかならない.論文や文章を書くときは、まず考えを記録し、それを彩る.
    関数も同じです.最初の作成は長くて複雑で、インデントと繰り返しのループがたくさんあります.しかし、不器用なコードをテストするユニットテストの例を作成しました.
    次に、コードを切り取り、関数を開き、重複データを除去し、目標を一つ一つ達成します.