2章 意味のある名前
Clean Code アジャイルソフトウェア達人の技の本を整理した内容です
概要
💡 ソフトウェアでは名前が至る所で使われます。変数、関数、引数、クラス、パッケージなどにも名前が付けられます。また、ソースファイル、ソースファイルが含まれるディレクトリ、jar、warファイルにも名前が付けられます。
したがって、名前をうまく付けることで、多くのことがスムーズになります。
この章では、名前をうまく付けるための簡単な規則をいくつか紹介します
1. 意図を明確にする
皆さんも経験しているように、良い名前を付けるには時間がかかりますが、良い名前によって節約できる時間はそれ以上です。
変数や関数、クラスの名前は次のような大きな質問にすべて答える必要があります。
- 変数の存在理由は?
- 実行機能は?
- 使用方法は?
もし名前でこれらの項目に答えられない場合や、コメントが必要な場合は、意図が明確になっていないということです。
良くない例
public class User {
int a;
String n;
public void m() {
// メソッドが何をするのか分かりません
}
}
コメントがないなら、メソッドが何の機能を持っているのか確信できません。
こう書く人はほとんどいませんが、理解を助けるために極端な例を挙げました。
次に、良い例を見てみましょう。
良い例
public class UserProfile {
int userAge;
String userName;
public void updateUserName(String name) {
this.userName = name;
}
}
2. 誤解を招く情報を避ける
プログラマーはコードに誤解を招く手がかりを残してはいけません。誤解を招く手がかりはコードの意味を曖昧にし、プログラマーに誤った情報を与えることになります(誤解を招く手がかり = 曖昧な名前)。
誤解を招かない変数は明確で、意味の伝達と意図の把握がしやすいです。
良くない例
int cnt=0;
int calc(int a, int b){...}
cnt
はカウントを意味しますが、何をカウントしているのか分かりますか?
calc
は計算を意味しますが、加算なのか乗算なのか、何を計算しているのか分かりますか?
良い例
int itemCnt = 0;
int multiply(int a, int b){...}
3. 意味を持つ区別をする
単に動作させるだけのプログラマーは、自分自身で問題を引き起こすことになります。
例えば、同じ範囲内で異なる2つの概念に同じ名前を使用してはいけません。
public class Order{
String id;
}
public class Member{
String id;
String name;
}
上の例では、Order.id
とMember.id
は明らかに異なる概念です。この場合、変数名をorderId
とmemberId
にする必要があります。
逆に、同じ概念に2つの名前を使用するのも避けるべきです。
MemberクラスのnameをnameStringとした場合、name
とnameString
は何が違うのでしょうか?
また、Member
クラスとMemberObject
というクラスがある場合、特定の名前を持つMember
を見つける際に、どちらのクラスを探すべきでしょうか?
これを防ぐために、概念に応じて変数名を区別する必要があります。
(同じ概念には同じ変数名を使いましょう)
4. 発音しやすい名前を使う
発音しにくい変数名は、議論が難しくなります。これは、逆に発音しやすい変数名が議論に有利だということです。
良くない例
Date genymdhms = new Date();
Date modymdhms = new Date()
良い例
Date generationTimestamp = new Date();
Date modificationTimestamp = new Date();
5. 検索しやすい名前を使う
1文字だけの名前や定数は、テキストコードの中で目立ちにくいという問題があります。
MAX_USER_COUNT
は検索しやすいですが、q
, w
, e
, r
のような一文字の変数名は検索が難しいです。
この観点からすると、長い名前は短い名前よりも優れています。
ただし、著者は簡単なメソッドのローカル変数には一文字を使うこともあると述べています。
public int multiply(int a, int b) {
int r = a * b;
return r;
}
しかし、変数や定数を複数のコードで使用する場合、検索しやすいように長い名前を使用するのが望ましいです。
public static final int realDaysPerIdealDay = 4;
public static final int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j=0; j<NUMBER_OF_TASKS; j++) {
int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
int realTaskWeeks = realTaskDays / WORK_DAYS_PER_WEEK;
sum += realTaskWeeks;
}
上記のコードでは、realDaysPerIdealDay
とWORK_DAYS_PER_WEEK
は定数であり、数値の代わりに使用することで検索が容易になるだけでなく、その数値に意味を与えることができます。
6. エンコーディングを避ける
エンコーディングの意味は、データの互換性を保つためにデータを特定の形式に変換する過程を指します。よく知られているエンコーディングの種類には、ASCIIやUTF-16などがあります。
しかし、ここでいうエンコーディングとは、コードを記述する際に使われる略語、特殊文字、特定の規則を指します。
良くない例
public class Part{
private String m_dsc; // "m_はメンバーを意味
void setDescription(String m_name){
m_dsc = m_name;
}
}
良い例
public class Part{
private String description;
void setDescription(String name){
description = name;
}
}
大きな違いはないと感じるかもしれませんが、m_
という特定の規則がない下のコード例の方が、脳が情報を受け取る際により理解しやすいです。
6-1. インターフェースクラスと実装クラス
時にはエンコーディングが必要な場合もあります。
例えば、インターフェースクラスと実装クラスを考えてみましょう。
ユーザーサービスのインターフェースクラスの名前はuserService
となり、そのインターフェースの実装クラスはuserServiceImpl
というように、impl
エンコーディングが付くことになります。
7. 自分の記憶力を誇示しない
専門家は明確さが最優先であることを理解しています。
これは、誰が見ても意図が理解できるコードを書くことを意味します。
値を更新する関数の名前をu
とした場合、作成者は理解できるかもしれませんが、他の人には理解しにくいでしょう。→u
は明確ではありません。
7-1 名前について
クラス名
やオブジェクト名
は名詞や名詞句が適しています。
-
Customer
,WikiPage
,Account
などが良い例です。 -
Manager
,Processor
,Data
,Info
のような言葉は避け、クラス名に動詞を使わないでください。- これらの言葉はあまりにも抽象的です。
メソッド名
は動詞や動詞句が適しています。
-
saveCustomer
,deletePage
などが良い例です。 - アクセサは
get
を使用:getName
- 変更者は
set
を使用:setName
- 条件付きメソッドには
is
を使用:postCheck.isPost()
7-2 奇抜な名前は避ける
userInBlackHole()
- ブラックホールに入った物質は消えるため、このメソッドはユーザーを削除する機能を果たします。
このようなメソッド名よりも、deleteUser()
の方がはるかに良いです。
8. 一つの概念に一つの単語を使用する
抽象的な概念に一つの単語を選び、それを貫きましょう。
例えば、ユーザーの名前を示す変数名をuserName
, name
, userNm
などに分けず、一つに統一しましょう。
クラス名も同様です。
Controller
, Manager
, Driver
を混ぜて使った場合、違いを区別しやすいでしょうか?
一貫性のある語彙は、コードを使用するプログラマーにとって歓迎される贈り物です。
8-1. 言葉遊びをしない
「一つの概念に一つの単語を使用する」というルールに従った結果、複数のクラスにadd
というメソッドが生まれました。
すべてのadd
メソッドが同じ意味、例えば2つの引数を加えて返すという意味なら問題ありません。
しかし、リストに値を1つ追加するメソッドをadd
と呼んでもいいのでしょうか?
public class ListManager {
List<Integer> a = new ArrayList();
public void addValue(String element) {
a.add(element);
}
}
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
2つのメソッドは異なる概念(コンテキスト)を持っています。したがって、リストに値を追加するメソッドはappend
やinsert
とするのが正しいです。
9. 問題領域から名前を取る
(問題領域とはプログラマー以外の領域を指し、例えば病院システムの開発では病院に関連する用語を指します。)
適切なプログラマー用語がない場合は、問題領域から名前を取ってきましょう。そうすることで、コードを保守するプログラマーが、その分野の専門家に意味を尋ねて把握できるようになります。
例
public class UserService{
public void processUser(User user) {
// 患者入院処理ロジック
}
}
public class PatientService {
public void admitPatient(Patient patient) {
// 患者入院処理ロジック
}
}
患者をUser
と呼ぶよりも、Patient
と名付ける方が、そのメソッドについて理解しやすいでしょう。
10. 意味のあるコンテキストを追加する
自明で意味が明確な名前がないわけではありませんが、大多数の名前はそうではありません。
(userName
は意味が明確ですが、name
はそうではありません)
意味のあるコンテキストとは、コードが書かれた理由や意図、目的などを明確にすることを指します。
これにより、他の開発者がコードを読み解く際に、その動作を容易に理解できるようになります。
良くない例
public class UserData {
private String name;
public String getName() {
return name;
}
// どの名前を取得し、削除するのかが分からない
}
良い例
public class UserData {
private String userName;
public String getUserName() {
return userName;
}
}
11. 不必要なコンテキストを削除する
- オブジェクトが何かを伝えている場合、変数名の前に不必要なものを追加する必要はありません。
良くない例
public class Car {
private String carMake;
private String carModel;
private String carColor;
}
public String setCollor(car, color) {
car.carColor = color;
}
良い例
public class Car {
private String make;
private String model;
private String color;
}
public String setColor(car, color) {
car.color = color;
}