Clean Code アジャイルソフトウェア達人の技の本を整理した内容です
第5章 フォーマットを整える (Formatting)
誰かが私のコードを見た時、コードがきれいで、一貫性があり、細かいところまで注意が行き届いていて、秩序だっていると感心してほしい。プロが書いたという印象を与えたい。そのためには、まず定められたフォーマットをしっかりと守ることが必要です
1. フォーマットを合わせる目的
なぜコードのフォーマットをわざわざ整える必要があるのか?
他人のコードを読むのは難しい
他の人が書いたソースコードを修正するためにファイルを開いたところ、読みにくいコードが散在していたら、苛立ちが込み上げてくるでしょう。
フォーマットが整ったソースコードは、開発者同士のコミュニケーションにおいて非常に重要な役割を果たします。
協力して進めるプロジェクトでは、できるだけフォーマットを整えてコードを書くことが開発者の義務です。
後で誰かが、あるいは自分自身がリファクタリングや修正を行う際、フォーマットが整ったコードは迅速に理解し、修正しやすくなります。
これは、プロジェクトの成功や品質に大きく関わります。
2. 適切な行の長さを維持せよ
短いコードでも十分に大規模なプロジェクトを作ることができる
上の図は、大規模プロジェクト 7 つの各プロジェクトにおける最大ファイル長と最小ファイル長を示しています。
この図が私たちに伝えることは何でしょうか? それは、500 行を超えない、約 200 行のファイルでも大規模なシステムを構築できるという点です。一般的に、1000 行のコードよりも 200 行のコードの方が理解しやすいということです。
2-1. 新聞の記事のように書け
とても良い新聞を読んでいると想像してみましょう。おそらく次の条件を満たしているはずです。
- その新聞の最上部には、記事を数語で要約する見出しがある。
- 読者はその見出しを見て、記事を読むかどうか決める。
- 記事を上から下に読むと、記事の詳細が徐々に明らかになる。
コードも新聞と同じように書きます。
- コードの最上部である関数名やクラス名は、そのコードが実行する主要な作業を要約する役割を持ちます。
- 開発者は、関数やクラスの宣言部分を見て、その部分のコードを詳細に調べるか、それとも他の部分を探すかを決定します。
- コードも新聞のように、上から下へと読み進めることで、全体的なロジックから細かい実装まで、次第に詳しい内容が明らかになります。
たとえば、getUserAge()という関数がユーザーの年齢を返すはずなのに、突然ユーザーの名前を返すような内容だったら、 誰もそのコードを読みたくなくなるでしょう
2-2. 概念は空行で区切れ
同じ概念はまとめて、区切るところはきちんと空行を入れよう
ほとんどの場合、コードは上から下へ、左から右へと読みます。
一連の行のグループは、1 つの完結した概念を表現しています。それぞれの概念や関連性の間に空行を入れて区切ることで、コードは見やすくなります。
2 つのコードを比較してみてください。
例 1)
public void processOrder(Order order) {
validateOrder(order);
int availableStock = inventoryService.getAvailableStock(order.getItemId());
if (availableStock < order.getQuantity()) {
throw new IllegalStateException("Not enough stock available");
}
inventoryService.updateStock(order.getItemId(), order.getQuantity());
String confirmationMessage = "Your order has been processed";
emailService.sendEmail(order.getCustomerEmail(), confirmationMessage);
orderRepository.save(order);
}
例 2)
public void processOrder(Order order) {
validateOrder(order);
int availableStock = inventoryService.getAvailableStock(order.getItemId());
if (availableStock < order.getQuantity()) {
throw new IllegalStateException("Not enough stock available");
}
inventoryService.updateStock(order.getItemId(), order.getQuantity());
String confirmationMessage = "Your order has been processed";
emailService.sendEmail(order.getCustomerEmail(), confirmationMessage);
orderRepository.save(order);
}
同じ概念でまとめるだけで、非常に良く書かれていると感じませんか?!!
2-3 垂直距離
長いソースコードの内容を把握しようとすると、コードを上下にスクロールして確認することになります。
この作業にかかる時間を短縮するために、いくつかのルールがあります。
a. 変数宣言
変数は初めて使用する位置にできるだけ近く宣言します。
上のコードでは、availableStock
と confirmationMessage
が使用される直前に宣言されています。
b. インスタンス変数
インスタンス変数はクラスの最初に宣言します。
よく設計されたクラスは、多くのメソッドがインスタンス変数を使用するためです。
(これは言語によってルールが異なる場合がありますが、重要なのは、インスタンス変数を誰もがわかりやすい場所にまとめることです。)
c. 依存メソッド
あるメソッドが別のメソッドを呼び出す場合、2 つのメソッドは縦に近い位置に配置する必要があります。
このとき、依存メソッドの順序は呼び出される順に従います。
下記の processOrder
メソッドは、validateOrder()
と sendEmail()
を順に呼び出すため、依存メソッドの順序もそれに従うべきです。
例 3)
public void processOrder(Order order) {
validateOrder(order);
int availableStock = inventoryService.getAvailableStock(order.getItemId());
if (availableStock < order.getQuantity()) {
throw new IllegalStateException("Not enough stock available");
}
inventoryService.updateStock(order.getItemId(), order.getQuantity());
String confirmationMessage = "Your order has been processed";
sendEmail(order.getCustomerEmail(), confirmationMessage);
orderRepository.save(order);
}
public void validateOrder(Order order){...}
public void sendEmail(String email, String confirmationMessage){...}
...
d. 概念的類似性
概念的に互いに密接であれば、近くに配置します。
例えば、参照、更新、削除を行うメソッドがある場合、参照メソッドは参照メソッド同士でまとめ、更新は更新、削除は削除といったように、概念的に密接なもの同士をまとめるのが良いです。
つまり、機能ごとに近くにまとめるのが良いとされています。
しかし、私はドメインごとにまとめるのが良いと考えており、コードを書く際にはドメインごとにまとめます。
例 1) 機能ごとにまとめた場合
// Getters
public User getUserById(int userId){...}
public Order getOrderById(int orderId) {...}
// Updaters
public void updateUser(User user) {...}
public void updateOrder(Order order) {...}
// Deleters
public void deleteUser(User user) {...}
public void deleteOrder(Order order) {...}
例 2) ドメインごとにまとめた場合
// User
public User getUserById(int userId) {...}
public void updateUser(User user) {...}
public void deleteUser(User user) {...}
// Order
public Order getOrderById(int orderId) {...}
public void updateOrder(Order order) {...}
public void deleteOrder(Order order) {...}
しかし、最も重要なのはチームの規則に従うことです。
ここまで、縦方向の規則について学びました。
この後は横方向の規則に関する内容ですが、「行を長くしすぎない」「コードを詰めて書かない」といった基本的な内容なので、ここでは詳しく取り扱いません。
お読みいただきありがとうございました。