メソッドの理想的な行数に関する神話

関数にn行を超えるコードまたはm行未満のコードがある場合、デザインの点で関数に問題があるという神話があります。 例えば、出版物「Reflections on Design Principles」の著者は、 メソッドの行数は必要ですが、良い設計には十分な条件ではないと述べています。 この記事では、関数が神話として特定のサイズである必要があると考える理由を説明し、それを証明する例を挙げますが、まず、この神話の人気の理由を見てみましょう。



神話の人気の理由


実際のアルゴリズムをほとんど使用する場合、通常は3〜10の少数の操作に簡単に分割でき、これらの操作は独自の微小な目的に役立ちます。 たとえば、テーブルの近くに立っている椅子に座るには、1)椅子を見る必要があります。 2)椅子に行きます。 3)椅子を動かします。 4)椅子に座ってください。 このようなアクションの説明は非常に理解しやすいものであり、各アクションを見ると、その背後に隠れているものと、それを実装するために必要なおよそのステップを理解できます。 これは良いデザインの例です。 「椅子を見る」ステップの代わりに、目の筋肉に負担をかけるいくつかの操作があり、「椅子に行く」の代わりに、ルートの絶え間ない編集を伴う漸進的な進行のサイクルがある場合、このアルゴリズムを理解することは困難です。 アルゴリズムの詳細をすべて覚えていても、すでに問題があります。 これは、複雑さを適切に制御できない貧弱なデザインの例です。 この場合、1つの機能で椅子と人の距離を縮めるという1つの目的に役立つ操作を選択することをお勧めします。



たとえば、スクランブルエッグを揚げたり、映画館に行くなど、他のアルゴリズムを使用する場合、さまざまなミクロの目的に役立つ最大10個の小さな操作を区別できます。 操作が10をはるかに超える場合に、すぐに例を考え出すことに同意します。それは非常に難しいことではありませんか。 それでもかなりの数の操作がある場合は、おそらくいくつかの共通の目標を見つけることができます。または、実際にはアルゴリズムのメイン部分ではないエラー処理に夢中になりすぎている可能性があります。



神話反論


抽象化の最上位に、より大きな操作に結合できない多数の操作が存在する(アルゴリズムの主な機能が多くのコード行に膨張する結果となる)人生のアルゴリズムを思い付くのは、かなり困難ですが可能です。 たとえば、アボリジニの部族の儀式ダンスは、次のアクションで構成されています。1)座る。 2)楽しい時間を過ごす。 3)うなり声; 4)手を振る。 5)立ち上がる。 6)ジャンプ...および別の100または500の無秩序な非体系的なアクション。
抽象化の最上位は何ですか
抽象化の第1レベルは、コードのグローバルな目的です。これは、優れた設計で、アルゴリズムの主な機能の名前と一致します。この場合、「Call rain」としましょう。 抽象化の最初のレベルは縮退しており、常に1つの操作があります。したがって、考慮から除外し、より高いレベルで2番目を意味します。 抽象化の最高(2番目)レベルには、「Crouch」や「Pokahtakhat」などの操作があります。 同時に、「しゃがみ」の目標は、体の位置を変えることであり、「楽しい時間を過ごす」ことは、鶏の音を模倣した音を出すことです。 これらの目標は異なります。それらの間で共通の目標を探す場合、このコンテキストでは「コールレイン」という共通の目標は1つしかありません。そのため、「コールレイン」内で呼び出される別の機能に分けることはできません。 「しゃがむ」と「息をする」は、「雨の原因」に直接あるべきです。 したがって、それらは最高レベルの抽象化にあります。




それでも関数をn行未満のコードにしたいですか? 次に、方法を1に分割する必要があります。1)儀式ダンスの最初の部分を実行します。 2)2番目の部分を実行します-など。これは、デザインが悪い例です。これは、この機能またはその機能が何を隠しているかが明確ではないためです。 同様の状況の例を挙げますが、すでにプログラミングの分野からです。 特定のサイトからページをダウンロードして解析し、購入に関する情報を取得するモジュールがあり、その中にプロトコル、ロットなどが存在するとします。 メインモジュールメソッドは次のようになります。



1)サプライヤーに関する情報を見つけます。

2)調達ロットに関する情報を見つけます。

3)調達オークションに関する情報を見つけます。

4)調達プロトコルに関する情報を見つけます。

5)購入ステータスが最後に変更された日付に関する情報を見つけます。

...および同様の方法。 さらに、各メソッドは異なるページをダウンロードし、さまざまな解析アルゴリズムを適用します。 各操作は個別の方法で割り当てられるに値し、それらの一部をフレンドリ名を持つ個別の機能に結合することは一切機能しません(「サプライヤーとロットとオークションに関する情報を検索する」オプションを提供しないでください)。 対象モデルとソースサイトのページ数は無制限に増やすことができます(サイトのフロントエンド開発者の数も増え、サイトのさまざまなページにますます具体的に貢献します)。 神話に反論する別の例は、暗号アルゴリズムのファミリーです。 名前を付けない許容メソッドサイズの最大数nが大きいほど、暗号化アルゴリズムをより長く開発できます。



神話の裏側


この神話には別の解釈があります-メソッドのコード行がm行(たとえば50)未満の場合、何か問題があります。 このような視点はどのようにして生じるのでしょうか? クラスやメソッドの名前がエンティティの目的を反映していないか、完全に誤解を招くような一貫したアーキテクチャを持たないコードを想像してください。 おそらくコードは元々良かったのですが、その後誰かが「購入ステータスが変更された最後の日付に関する情報を見つける」機能を変更し、データベースに情報を保存し、ユーザーに通知を電子メールで送信しますが、機能の名前は変更されていません。 または、誰かが変更日を見つけるためにアルゴリズムに変更を加えたが、この関数ではなく、他の場所で行った場合、関数の名前を「購入ステータスのPARTの最終変更日に関する情報を見つける」または「イベントログに関する情報を見つけてください」(これは変更の日付を検索する操作のPARTであり、メソッドはそれに応じて呼び出される必要があります)、残念ながら、関数の名前は変更されていません。 その結果、そのようなコード内のメソッドの名前には信頼がなく、実際に何が起こるかを知るには、それらのそれぞれに該当する必要があります。 また、コードが多数のメソッドに断片化されており、ネストの深さが大きい場合、さらに深くする必要があります。その結果、迷路のようにコードが混乱しやすくなります。 しかし、クラスのすべてのコードが1つの巨大な関数に含まれている場合、少なくとも目に見えるようになり、明確に、そして意図的に誤った関数名が混乱することはありません。



次に、マーカスという架空のプログラマーを想像してください。 マーカスはデザインの研究に特に熱心ではなく、上記の厄介なコードを毎日使用しています。 徐々に、マーカスは「大きなコード」を理解しやすくなることに気づき始め、「細かく分割された」コードは頭痛に関連付けられ始めます。 その後、誰かが「余分なエンティティを作成しない」という原則について簡単に彼に話します。 どのエンティティが余分で、どのエンティティがそうではないのか、マーカスは説明できませんが、原則を役立てます。 その後、マーカスはどこかからKISSの原則を見つけ、「エンティティが少ないほど理解しやすい」、「エンティティが少ないほど、KISSに対応するコードが多くなる」と判断します。



これは Marcusという名前のキャラクターを持つ記事の例であり、どの燃料源を使ったオーブンでも、どのレシピでもあらゆる種類のベーカリー製品を焼く方法を知っているクラスを書いたもので、このクラスにはメソッドが1つしかありません。 私が理解しているように、彼はプログラム全体に2つのクラスがあります-パン(実際にはパイになることがあります)とマネージャー(会議で「私は何でもできます!」と言うことができます。 私たちのマーカス(この記事から)は、これがベストプラクティスであり、KISSの原則に従っていることに同意し、信じています。1000行のコードで神オブジェクトを作成しない場合、彼から学ぶべきことがあります。

個人的には、メソッドはm行よりも大きくなければならないというルールはなく、99.9%のケースでは、内部で何が起こっているのか、そのコントラクトは何なのか、そして何が目的なのかを伝えるきちんとした小さな関数を書くことができる彼らは仕えます。 同時に、必要な機能を見つけるのにそれほど時間はかからず、コード全体を調べる必要もありません。



しかし、何が必要ですか?


私たちはすでにそれをしない方法を知っています-メソッドの行数を盲目的に信頼します。 論理的な疑問が生じます-しかし、それはどのように必要ですか? 関数に何かを追加したり、関数から何かを削除したりする必要があることを確認する方法は? 「低結合と高凝集」の原則と2つの匂い:「ショットで撮影する」と「修正を分散させる」は、私たちが見つけるのに役立ちます。 機能のタイプを変更するときに、このエンティティの一部と別のエンティティの一部を修正する必要がある場合、コードは「分数で撮影」と「低凝集性」で始まり、これら2つのエンティティを1つにマージするのが良いことを意味します 機能のタイプを変更するときに、エンティティのその部分が常に変更され、エンティティのこの部分が変更されない場合、コードは「変更の分散」のような匂いがし、エンティティを2つに分割する価値があります。 明確にするために、記事の冒頭から少し修正した例を使用します:椅子に近づくロボットの移動方法を変更する場合、ルートと移動の選択に関するアルゴリズムの部分を常に変更します(ロボットが地面、地下、または上を移動するかどうかによって異なります)グラウンド)、「テーブルに近づきます」という別の機能を強調表示する必要があります。 その結果、1つのエンティティがあった場合、2つが表示されます。 また、エンティティのわかりやすい名前を付ける必要があります。そうすることで、このエンティティが何をするか(名前はしない)を名前だけで理解できるようになります。



PS上記はすべて私の個人的な意見です。 すべてのキャラクターは架空のものです。



ここで、記事は終了し、読者のリクエストで追加された、分解するのが難しい別の長いアルゴリズムの例を示します。 したがって、よく知られた1つの暗号化アルゴリズムを使用してテキストを暗号化するには、次のものが必要です。

1)ソースを64ビットのブロックに分割します

2)そのようなブロックごとに

{

2.1)特定のアルゴリズムに従って、ブロック内のビットを適切な場所に再配置します

2.2)ブロックを32ビット長の2つのブロックに分割します(以降-左右)

2.3)16回繰り返す:

{

2.3.1)特定のアルゴリズムを使用して、暗号化キーKと反復回数からKiを計算します。

2.3.2)特定のアルゴリズムに従って、長さ32の右ブロックから長さ48のブロックEを計算します。

2.3.3)F = KiとEのビット単位の合計

2.3.4)長さ32の左ブロック=最後の反復での長さ32ビットの右ブロック。

2.3.5)右ブロック32 =最後の反復で左ブロック32、ビット単位でFに追加

2.3.6)暗号化結果の最後に左ブロックを追加します

2.3.7)暗号化結果の最後に正しいブロックを追加します

}

}

ウィキペディアのKi計算アルゴリズムの説明がモニターに合わなかったので、プログラムではこれが1行のコードになるとは思いません。 関数「Kiを計算」を作成する場合、これは悪い設計になります。 この関数が何をするのか、Kiが何であるかは不明です。 ただし、Kiに一般名があり、Kiに一般的な抽象概念がある場合、「Kiを計算する」関数には存在する権利があります。 すべてを同じように分解するために、そのような抽象化のアルゴリズムの開発者自身が作成し、あまり成功していませんが、その名前は「アルゴリズムの最初の部分」と「そのような名前の部分」に似ています。 同意、ひどいデザイン。 ただし、このデザインは一般に受け入れられており、サブジェクトエリアであるため、使用する場合はすべて問題ありません。 ただし、元のアルゴリズムと同様の特性を持つ独自のアルゴリズムを作成するために、アルゴリズムをわずかに変更する必要があると想像してみましょう(これは有用です。攻撃者が「未知の科学」アルゴリズムを解読するのは難しいためです)。 新しいアルゴリズムでは、「そのような名前の一部」が変更されており、すでに「そのような名前の部分」ではありません。 この場合、与えられたアルゴリズムを小さな関数に分割せず、そのままにしておくことをお勧めします。そうしないと、1日(コードを書いてから1週間後)迷路のようにこのコードで混乱します。 ここで与えられるアルゴリズムはDESです。 「最初の部分」は「初期順列」、「このような名前の部分」は「Feistel関数」、Eは「拡張機能」です。 変更されたアルゴリズムでは、これはすべて異なります。 変更されたDESを部分的に分解することは可能です。たとえば、「ビットごとのブロックを追加」、「順列行列からのブロックの置換」などのメソッドを選択できますが、「ブロック64の暗号化」メソッドは引き続き有効です。淫らに大きい。 「長さ32のブロックを暗号化する」方法を選択するのは悪い考えです。 解読できません。 暗号化と復号化はブロック64に適用できます。DESはアルゴリズムの中で最長ではないことを確信しています。最高レベルの抽象化で多数の操作を行うなど、より長いアルゴリズムを見つける(または考える)ことができます。友達。



All Articles