私は最近、いくつかのコードをリファクタリングしており、うまくできたと思いました。 980行のコードを450行に減らし、クラスの数を半分にしました。
これを同僚に見せたとき、これが改善であることに同意しなかった人もいます。
彼らは言った-"コードの少ない行は必ずしも良いとは限りません"
極端に長い行を書いたり、すべてを1つのメソッドにまとめて数行を保存したりする極端な場合があることは理解できますが、それは私が行ったことではありません。私の意見では、コードは十分に構造化されており、サイズが半分であるため、理解/保守が簡単です。
私は仕事を成し遂げるために必要な2倍のコードで誰もが働きたいと思う理由を理解するのに苦労しています。誰かが私の同僚と同じように感じ、より少ないコードでより多くのコードを使用するためのいくつかの良いケースを作ることができるかと思います?
痩せた人は、太りすぎの人よりも必ずしも健康ではありません。
980行の子供の物語は、450行の物理学の論文よりも読みやすくなっています。
コードの品質を決定する多くの属性があります。 Cyclomatic Complexity や Halstead Complexity のように、単純に計算されるものもあります。 凝集度 、読みやすさ、理解しやすさ、拡張性、堅牢性、正確性、自己文書化、清潔さ、テスト容易性など、他のものはより緩く定義されています。
たとえば、コード全体の長さを減らした一方で、不要な複雑さを追加し、コードをより暗号化した可能性があります。
長いコードを小さなメソッドに分割する 有益である可能性と同じくらい有害である可能性があります 。
同僚に、あなたのリファクタリング作業が望ましくない結果をもたらしたと考える理由について具体的なフィードバックを提供するよう依頼してください。
おもしろいことに、同僚と私は現在、リファクタリングの最中です増加クラスと関数の数を2倍弱減らしますが、コードの行は同じままです。だから私はたまたま良い例を持っています。
私たちのケースでは、実際には2層であるはずの抽象化の1層がありました。すべてがuiレイヤーに詰め込まれました。それを2つの層に分割することで、すべてがよりまとまり、個々の部分のテストと保守がはるかに簡単になります。
同僚を悩ませているのはsizeのコードではなく、別の問題です。彼らがそれを明確にできない場合は、古い実装を見たことがないかのように自分でコードを見て、単に比較するのではなく、独自のメリットで評価してください。長いリファクタリングを行うと、元の目標を見失い、物事をやりすぎてしまうことがあります。重要な「全体像」を見て、それを軌道に戻します。多分あなたがアドバイスを重視するペアのプログラマーの助けが必要です。
しばしばアルバートアインシュタインに起因する引用が思い浮かびます:
すべてをできるだけ単純にしますが、単純にはしません。
物事を切り詰めて行き過ぎると、コードが読みにくくなります。 「読みやすい/読みにくい」は非常に主観的な用語となる可能性があるため、私がこれが何を意味するかを正確に説明します。「このコードは何をするのか」を決定する上で熟練した開発者が持つ難しさの程度の尺度です。専門ツールの支援なしで、ソースを見るだけで。
JavaやPascalのような言語は、その冗長性で悪名高くなっています。人々はしばしば特定の構文要素を指して、「コンパイラーの仕事を簡単にするためにそこにあるだけだ」と嘲笑しています。これは多かれ少なかれです。 「ただ」の部分を除いてtrue。より明確な情報があるほど、コンパイラだけでなく人間もコードを読みやすく、理解しやすくなります。
var x = 2 + 2;
、x
が整数であることになっていることはすぐにわかります。しかし、私がvar foo = value.Response;
、foo
が何を表しているのか、そのプロパティと機能が何であるのかは、それほど明確ではありません。たとえコンパイラがそれを容易に推論できるとしても、それは人により多くの認知的努力を置きます。
プログラムは、人が読むために作成する必要があり、付随的にマシンを実行するためだけに作成する必要があることに注意してください。 (皮肉にも、この引用は、読むのが非常に難しいことで悪名高い言語に捧げられた教科書に基づいています!)冗長なものは削除することをお勧めしますが、仲間の人間が簡単にできるようにするコードは削除しないでください。書かれているプログラムに厳密に必要でなくても、何が起こっているのかを理解してください。
コードが長いほど読みやすくなる可能性があります。それは通常反対ですが、多くの例外があります-それらのいくつかは他の回答で概説されています。
しかし、別の角度から見てみましょう。新しいコードは、会社の文化、コードベース、またはロードマップについての追加の知識がなくても2つのコードを見るほとんどの熟練したプログラマーによって優れていると見なされます。それでも、新しいコードに反対する理由はたくさんあります。簡潔にするために、「新しいコードを批判している人々」と呼びますPecritenc:
それは完全に依存します。ブール変数を関数パラメーターとして許可しないプロジェクトに取り組んでいますが、代わりに各オプションに専用のenum
が必要です。
そう、
enum OPTION1 { OPTION1_OFF, OPTION1_ON };
enum OPTION2 { OPTION2_OFF, OPTION2_ON };
void doSomething(OPTION1, OPTION2);
よりもはるかに冗長です
void doSomething(bool, bool);
しかしながら、
doSomething(OPTION1_ON, OPTION2_OFF);
よりもはるかに読みやすいです
doSomething(true, false);
コンパイラは両方に対して同じコードを生成する必要があるため、短い形式を使用しても何も得られません。
どの種類のコードが優れているかは、プログラマーの専門知識と、使用するツールに依存する場合があります。たとえば、次の理由により、通常は不十分に記述されたコードと見なされるものが、継承を十分に活用する適切に記述されたオブジェクト指向コードよりも状況によってはより効果的である場合があります。
(1)一部のプログラマーは、オブジェクト指向プログラミングを直感的に理解していません。ソフトウェアプロジェクトの比喩が電気回路である場合、コードの重複が多数発生することが予想されます。多くのクラスで多かれ少なかれ同じメソッドを見たいと思うでしょう。彼らはあなたがくつろげるようになります。そして、何が起こっているのかを敵対的に感じる可能性があるかどうかを確認するために、親クラスまたは祖父母クラスのメソッドを検索する必要があるプロジェクト。親クラスがどのように機能するかを理解してから、現在のクラスがどのように異なるかを理解する必要はありません。現在のクラスがどのように機能するかを直接理解する必要があり、情報が複数のファイルに分散しているという事実が混乱していることがわかります。
また、特定のクラスの特定の問題を修正するだけの場合は、問題を基本クラスで直接修正するか、現在の対象クラスのメソッドを上書きするかを考えたくない場合があります。 (継承がなければ、意識的な決定を行う必要はありません。デフォルトでは、バグとして報告されるまで、同様のクラスの同様の問題を無視します。この最後の側面は、有効な引数ではありませんが、反対。
(2)一部のプログラマーはデバッガーをよく使用します。一般的に私はコードの継承と複製の防止にしっかりと取り組んでいますが、オブジェクト指向のコードをデバッグするときに(1)で説明したフラストレーションの一部を共有します。コードの実行を追跡すると、同じオブジェクト内にある場合でも、(祖先)クラス間をジャンプし続けることがあります。また、適切に記述されたコードにブレークポイントを設定すると、役に立たない場合にトリガーが発生する可能性が高くなるため、条件付きにする(実用的である)か、関連するトリガーの前に手動で何度も継続する必要がある場合があります。
より少ないコードは、より多くのコードよりも保守性/拡張性が低くなります。多くの場合、コードを簡潔にするためにリファクタリングすると、「不要な」コード構成要素がLoCのために削除されます。問題は、パラレルインターフェイス宣言、抽出されたメソッド/サブクラスなど、これらのコード構成が必要です。このコードが現在実行している以上のことを実行したり、別の方法で実行したりする必要がある場合です。極端な場合、特定の問題に合わせてカスタマイズされた特定のソリューションは、問題の定義が少しだけ変更された場合、まったく機能しない可能性があります。
一例;整数のリストがあります。これらの整数のそれぞれは、1つを除いて、リスト内で重複した値を持っています。アルゴリズムは、対応のない値を見つける必要があります。一般的な場合の解決策は、リストに重複がない番号が見つかるまで、すべての番号を他のすべての番号と比較することです。これは、N ^ 2回の演算です。ハッシュテーブルを使用してヒストグラムを作成することもできますが、これはスペース効率が非常に悪いです。ただし、ビット単位のXOR演算; XORすべての整数を実行中の「合計」に対して開始することで(ゼロ)、そして最後に、実行中の合計は対になっていない整数の値になります。非常にエレガントです。要件が変更され、リスト内の複数の数値が対になっていないか、整数にゼロが含まれている可能性があります。ここでプログラムはガベージまたはあいまいな結果を返します(それがゼロを返す場合、それはすべての要素がペアになっていることを意味しますか、それともペアになっていない要素がゼロであることを意味しますか?)これは、実際のプログラミングにおける「賢い」実装の問題です。
コンピュータコードは多くのことをする必要があります。これらのことを行わない「ミニマリスト」のコードは、良いコードではありません。
たとえば、コンピュータプログラムは可能な限りすべてをカバーする必要があります(または最低限、可能性のあるすべてのケースをカバーします)。 1つのコードが1つの「基本ケース」のみをカバーし、他のコードを無視する場合、たとえそれが短いものであっても、それは良いコードではありません。
コンピュータコードは「スケーラブル」である必要があります。暗号化されたコードは、1つの専用アプリケーションでのみ機能する可能性がありますが、より長く、よりオープンエンドのプログラムでは、新しいアプリケーションを簡単に追加できる可能性があります。
コンピュータコードは明確であるべきです。別の回答者が実証したように、ハードコアのコーダーが、仕事を行う1行の「アルゴリズム」タイプの関数を生成することは可能です。しかし、平均的なプログラマーにとって明確になる前に、ワンライナーを5つの異なる「センテンス」に分割する必要がありました。
結束が問題になるかもしれません。
たとえば、Webアプリケーションで、すべての製品にインデックスを付ける管理ページがあるとします。これは、ホームページの状況で使用するのと基本的に同じコード(インデックス)で、製品をインデックスに登録するだけです。
DRYと洗練された状態を維持できるようにすべてを部分化することにした場合、ユーザーがブラウジングしているのが管理者かどうかに関する多くの条件を追加し、不要なコードを散らかす必要があります。これは、デザイナーとは言い難くなります。
したがって、このような状況では、コードがほとんど同じであっても、それが別のスケールになり、ユースケースがわずかに変わる可能性があるという理由だけで、条件とifsを追加することで、それぞれを追跡するのはよくありません。したがって、DRYコンセプトを捨てて、コードを保守可能な部分に分割することは、良い戦略です。