\r\n\r\n
コード臭とは、コードベースの全体的な構造や設計に深い問題があることを示すと思われるコードの一部や一般的なコーディングパターンを指します。
コードの匂いは、コードの一部をリファクタリングする必要があることを示すものだと考えてください。これは、コードに欠陥があったり、機能的でないからではなく、通常、臭いコードはうまく機能します。しかし、臭いコードは保守や拡張が難しいことが多く、(特に大規模なプロジェクトでは)技術的な問題につながる可能性があるのです。
この記事では、最も一般的な10のコード臭に焦点を当て、何を探すべきか、そしてどのようにそれらを取り除くかについて説明します。もしあなたが新人プログラマーなら、これらを避けることで、あなたのコードは格段に良くなりますよ。
ご質問
タイトカップリングとは、2つのオブジェクトがデータや機能を互いに依存しあっていて、一方のオブジェクトを変更するともう一方のオブジェクトを変更しなければならないような状態を指します。2つのオブジェクトがあまりにも密に結合している場合、コードを変更することは悪夢となり、変更のたびにバグが発生することになります。
例えば、こんな感じです。
class Worker {
Bike bike = new Bike();
public void commute() {
bike.drive()。
}
}
この場合、作業者と自転車が密接に結びつきます。ある日、自転車ではなく、自動車で通勤したくなったらどうしますか?労働者階級に入り込み、自転車関連のコードをすべて自動車関連のコードに置き換える必要があります。面倒だし、ミスも多い。
ソリューション
抽象化レイヤーを追加することで、カップリングを緩めることができます。この場合、労働者階級は自転車だけでなく、自動車や、おそらくトラック、あるいはバイクにも乗りたいと考えているのです。これって全部乗り物ですよね?したがって、必要に応じて異なる車種を**交換できるような車両インターフェイスを作成します:。
class Worker {
車両車両。
public void changeVehicle(Vehicle v) {.
vehicle = v;
}
public void commute() {
vehicle.drive()。
}
}
インターフェース Vehicle { {
void drive();
}
class Bike implements Vehicle {
public void drive() {
}
}
class Car は Vehicle { を実装しています。
public void drive() {
}
}
ご質問
神オブジェクトとは、あまりにも多くの変数や関数を含む巨大なクラス/モジュールのことです。それは「知りすぎている」「やりすぎている」ということであり、これには2つの理由がある。まず、他のクラス/モジュールがこのクラス/モジュールにデータを過度に依存している(タイトカップリング)。第二に、すべてを一カ所に詰め込むため、全体の構成がごちゃごちゃしてしまうことです。
ソリューション
神様をオブジェクトとして捉え、神様が持つ問題に応じてデータや機能を分離し、そのグルーピングをオブジェクトに変換する。神様のオブジェクトがある場合は、小さなオブジェクトをたくさん合成したほうがよいかもしれません。
例えば、大規模なユーザークラスがあるとします。
class User {
public String username;
public String password;
public String address;
public String zipcode;
public int age;
...
public String getUsername() {.
ユーザー名を返します。
}
public void setUsername(String u) {.
ユーザー名 = u;
}
}
という組み合わせで変換することができます。
class User {
資格証明書資格証明書
プロフィールの紹介です。
...
}
class Credentials {
public String username;
public String password;
...
public String getUsername() {.
ユーザー名を返します。
}
public void setUsername(String u) {.
ユーザー名 = u;
}
}
次にログイン処理を変更する必要があるとき、多数のユーザークラスを横断する必要はありません。なぜなら、Credentials クラスは管理がはるかに簡単だからです
ご質問
長い関数とは、まさにその名の通り、長すぎる関数のことです。関数に「長すぎる」コード行数というのは特にありませんが、見ればわかると思います。これは、ほとんど神オブジェクト問題の厳密版で、長い関数に責任がありすぎるということです。
ソリューション
長い機能をいくつかのサブ機能に分解し、それぞれが1つのタスクや問題を処理するように設計する必要があります。理想的には、元の長い関数がサブ関数呼び出しのリストとなり、コードがより明確で読みやすくなることです。
ご質問
関数(またはクラスのコンストラクタ)が引数を取りすぎる理由は2つあります。まず、コードが読みづらくなり、テストが難しくなります。しかし、第二に、より重要なことは、機能の目的があまりにも曖昧で、多くの責任を処理しようとしすぎていることを示しているのではないでしょうか。
ソリューション
引数リストについては「多すぎる」というのは主観的なものですが、3つ以上の引数を持つ関数については、注意を払うことをお勧めします。もちろん、関数が5つ、あるいは6つの引数を持つことが理にかなっている場合もありますが、それはそうする正当な理由がある場合のみです。
ほとんどの場合、1つではなく、2つ以上の異なる機能にコードを分割するのがベストです。長い関数」のコード臭とは異なり、サブ関数に置き換えるだけでは解決しない。関数そのものを、別々の責任を持つ別の関数に分割する必要がある。
ご質問
1文字または2文字の変数名。記述できない機能名。クラス名の過剰修飾。変数名に型が付与される(例:boolean変数:bu)。最悪なのは、1つのコードベースに異なる命名方式が混在していることです。これらのことは、読みにくく、理解しにくく、保守しにくいコードにつながる可能性があります。
ソリューション
変数や関数、クラスの名前を上手に選ぶことは、なかなか難しいスキルです。既存のプロジェクトに追加する場合は、そのプロジェクトを調べ、既存の識別子がどのように名付けられているかを確認します。スタイルガイドがある場合は、それを覚えておいて、それを守ること。新しいプロジェクトでは、独自のスタイルガイドを作成し、それを遵守することを検討してください。
一般に、変数名は短いが、説明的であるべきである。関数名は通常、少なくとも1つの動詞を持ち、その名前から何をする関数なのかがすぐにわかるようにすべきですが、あまり多くの言葉を詰め込むのは避けましょう。クラス名についても同様です。
ご質問
誰かが書いたコードをざっと読んで、ハードコードされた数字を見つけたとします。おそらく、if文の一部か、意味のない謎の計算の一部なのだろう。関数を修正する必要があるが、数値の意味がわからない。頭を掻く。
ソリューション
このようないわゆる「ファントムナンバー」は、コードを書く際に絶対に避けるべきものです。ハードコードされた数値は、書いている時点では意味がありますが、すぐに意味を失います。特に、他の人があなたのコードを保守しようとした場合、そうなります。
解決策としては、数値を説明するコメントを残すことですが、より良い方法は、マジックナンバーを定数変数(計算用)または列挙型(条件文やswitch文用)に変換することです。このマジックナンバーに名前をつけることで、コードの可読性が限りなく高まり、誤った変更も起こりにくくなります。
ご質問
深くネストされたコードを終わらせる方法には、主にループと条件文の2つがあります。深くネストされたコードが常に悪いわけではありませんが、解析が難しく(特に変数の名前が悪い場合)、さらに修正も難しいため、問題になることがあります。
ソリューション
もし、2重、3重、4重のループを書いていたら、コードがデータを見つけるために自分より遠くに到達しようとしすぎている可能性があります。その代わり、データを含む任意のオブジェクトやモジュールを関数で呼び出してデータを要求する方法を提供する。
一方、条件文が深くネストしている場合は、1つの関数やクラスで多くのロジックを扱おうとしているサインであることが多いようです。実際、深いネストと長い関数は、しばしば手を取り合って使う。もし、コードに大量のswitch文やネストされたIf-then-else文がある場合は、ステートマシンやポリシーパターンを実装する必要があるかもしれません。
特に経験の浅いゲームプログラマーに多いのが、このディープネストです
ご質問
例外は強力ですが、簡単に悪用される可能性があります。スローイングキャッチステートメントを正しく使用しない怠惰なプログラマーにとって、デバッグは不可能ではないにしても、より困難なものとなっています。例えば、キャッチした例外を無視したり、隠したりすることである。
ソリューション
キャッチした例外を無視したり隠したりするのではなく、せめて例外のスタックトレースを出力してデバッガが利用できるようにしてください。プログラムが黙って失敗するのは、将来的に必ず頭を痛めることになるのですまた、一般的な例外ではなく、特定の例外をキャッチすることが望ましい。例外処理を適切に行う方法については、こちらの記事で詳しく説明しています。
ご質問
全く同じロジックが、プログラムのいくつかの無関係な領域で実装されています。その後、そのロジックを変更する必要があることに気づきますが、そのロジックを実装した場所をすべて覚えているわけではありません。結局、8か所のうち5か所しか変えられず、エラーや動作の一貫性が保てない。
ソリューション
例えば、チャットアプリケーションを開発する場合、次のようなコードを書くとします。
String queryUsername = getSomeUsername();
boolean isUserOnline = false;
for (String username : onlineUsers) {.
if (username.equals(queryUsername)) {.
isUserOnline = trueとする。
}
}
if (isUserOnline) {.
...
}
コードの他の場所で、同じように "Is this user online?" を実行する必要があることに気づきます。をチェックします。ループをコピー&ペーストする代わりに、関数からループを取り出すことができます。
public boolean isUserOnline(String queryUsername) {
for (String username : onlineUsers) {.
if (username.equals(queryUsername)) {.
が真を返す。
}
}
は false を返します。
}
isUserOnline()チェックが、コードのどこでも使えるようになりました。このロジックを修正する必要がある場合は、このメソッドを調整すれば、このメソッドが呼び出された場所で適用されます。
ご質問
コードのどこにもコメントはありません。関数のドキュメントブロック、クラスの使い方の概要、アルゴリズムなどの説明もありません。よくできたコードにはコメントは必要ないと思うかもしれませんが、実は、どんなによくできたコードでも、理解するためには英語以上に精神的なエネルギーが必要なのです。
ソリューション
メンテナンスしやすいコードベースの目標は、コメントを必要としないほどよく書かれたコードであり、なおかつコメントがあるコードであるべきです。コメントを書くときは、そのコードが何をやっているかを説明するのではなく、なぜそのコードが存在するのかを説明するコメントを目標にします。コメントは心や正気を保つのに良いものです。無視しないでください。
当たり前のことですが、ほとんどのコード・ニオイは、優れたプログラミングの原則やパターンを誤解したり、無視したりしていることが原因です。例えば、ステミングの原則をしっかり守れば、コードの繰り返しはほとんどなくなりますし、単一責任の原則をマスターすれば、恐ろしい神オブジェクトを作ることはほとんど不可能になります。
また、プログラミングのより実践的な側面に目を向けた、よりきれいなコードの書き方の記事もおすすめです。自分のコードを一目で読めなくて、他の人が読めるわけがない。クリーンなコードは無味乾燥なコードです。
プログラミングで一番苦労することはなんですか?
写真提供:SIphotography/Depositphotos