12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【忙しい人のためのリーダブルコード要約】第I部 表面上の改善

Last updated at Posted at 2024-11-11

はじめに

こちらは著書「リーダブルコード」の学習記録です。
忙しい人のためにも、重要そうな部分を抜粋してまとめました。

こちらは本書における第Ⅰ部の要約です。
第II部の要約はこちらから
第Ⅲ部の要約はこちらから
第Ⅳ部の要約はこちらから

2章 名前に情報を詰め込む。

明確な単語を選ぶ

例えば、「get」はあまり明確な単語ではない

def GetPage(url):
....

このメソッドはどこからページを取ってくるのか?
ローカルキャッシュから?データベースから?
もしインターネットから取ってくるならば、DownloadPage()の方が明確だ。

tmpなどの汎用的な名前を避ける

エンティティの値や目的を表した名前を選ぼう。

(例)tmp
2つの変数を入れ替える古典的な例です。

if(right < left) {
  tmp = right;
  right = left;
  left = tmp;
}

この場合は、情報の一時的な保管のため、tmpという名前で全く問題ない。tmpという名前で「この変数には他に役割がない」という明確な意味を伝えている。
つまり、他の関数に渡されたり、何度も書き換えられたりはしない。

ただし、以下のtmp、テメーはダメだ。

String tmp = user.name();
tmp += " " + user.phone_number();
tmp += " " + user.email();
...
tmplate.set("user_info", tmp);

この変数にとって一番大切なことは、「一時的な保管」ではない。これをわかりやすくするには、user_infoのような名前に変えたほうが良いだろう。

名前の長さを決める

以下のような識別子は、誰もが嫌がるだろう。
newNavigationControllerWrappingViewControllerForDataSourceOfClass

どこの世界線の呪文詠唱だ。詠唱破棄したくなるじゃないか。

では、どれくらいの長さが適切なのか?それは、変数の使い方によって違ってくる。

スコープが小さければ短い名前でもいい

識別子の「スコープ」(その名前が「見える」コードの行数)が小さければ、多くの情報を詰め込む必要はない。
すべての情報(変数の型・初期値・破棄方法など)が見えるので、変数の名前は短くていい。

if(debug) {
  map<string,int> m;
  LookUpNamesNumbers(&m);
  Print(m);

ただし上記のmがクラスのメンバ変数やグローバル変数だった場合、mの型や目的がわからなくなるだろう。
識別子のスコープが大きければ、名前に十分な情報を詰め込んで明確にする必要がある。

3章 誤解されない名前

filter()

データベースの問い合わせ結果を処理するコードを書いてるとしよう。

results = Database.all_objects.filter("year <= 2011")

このresultsには何が含まれているだろうか?

  • 「year <= 2011」のオブジェクト
  • 「year <= 2011」ではないオブジェクト

どちらかよくわからないのは、filterが曖昧な言葉だからだ。これでは「選択」するのか、「除外」するのかわからない。
「選択」するのであれば、select()にしたほうがいい。「除外」するのであれば、exclude()にした方がいい。

ブール値の名前

ブール値の変数やブール値を返す関数の名前を選ぶときには、trueとfalseの意味を明確にしなければならない。

以下は危険な例だ。

bool read_password = true;

「read」をどう「読む」かになるけど、これには2つの解釈の仕方がある。

  • パスワードをこれから読み取る必要がある
  • パスワードをすでに読み取っている

代わりに、need_passworduser_is_authenticatedなどを使った方がいい。
ブール値の変数名は、頭にis・has・can・shouldなどをつけてわかりやすくする場合が多い。

それから、名前は否定形よりも肯定形にした方が声に出して読みやすい。

// 否定形
bool disable_ssl = false;

// 肯定形
bool use_ssl = true;

4章 美しさ

なぜ美しさが大切なのか?

以下のようなコードを使わなければならないとしよう。

class:class StatsKeeper {
public:
// doubleを記録するクラス
   void Add(double d); //と素早く統計を出すメソッド
   private: int count; /*それまでの個数*/
   public:
     double Average();
private: double minimum;
list<double>
  past_items
    ;double maximum;
};

これを理解するのには時間がかかると思う。では以下のキレイなバージョンならどうだろう。

class StatsKeeper {
public:
   void Add(double d);
    double Average();
    
private:
list<double> past_items
int count;

double minimum;
double maximum;
};

見た目が美しいコードの方が使いやすいのは明らかだ。

一貫性と意味のある並び

コードの並びがコードの正しさに影響を及ぼすことは少ない。

details = request.POST.get('details')
location = request.POST.get('location')
phone = request.POST.get('phone')
email = request.POST.get('email')
url = request.POST.get('url')

であれば、ランダムに並べるのではなく、意味のある順番に並べると良い。

  • 対応するHTMLフォームのinputフィールドと同じ並び順にする
  • 「最重要」なものから重要度順に並べる
  • アルファベット順に並べる

重要なのは、正しさ」よりも「一貫性

5章 コメントすべきことを知る

コメントの目的は、書き手の意図を読み手に知らせることである。

コメントするべきでは「ない」こと

コードを読んですぐわかることはコメントに書かない。

// Accountクラスの定義
class Account {
  public:
    // コンストラクタ
    Account();
    // profitに新しい値を設定する
    void SetProfit(double profit);

    // このAccountからprofitを返す
    double GetProfit();
};

自分の考えを記録する

それでは、何をコメントすべきなのか?
優れたコメントとは、「考えを記録する」ためのものである。
コードを書いているときに持っている「大切な考え」のことだ。

コードの欠陥にコメントをつける

コードが未完成の時は、例えば以下のように書いておくと良い

// TODO: JPEG以外のフォーマットに対応する
記法 典型的な意味
TODO: あとで手をつける
FIXME: 既知の不具合があるコード
HACK: あまりキレイじゃない解決策
XXX: 危険! 大きな問題がある

大切なのは、これからコードをどうしたいかを自由にコメントに書くこと。コードの品質や状態を知らせたり、改善の方向性を示したりできる。

読み手の立場となって考える

「全体像」のコメント

新しいチームメンバーにとって、最も難しいのは「全体像」の理解である。
データはどのようにシステムを流れているのかなど、システムを設計した人は、コメントを書かないことが多い。
あまりにも密接にシステムに関わり過ぎているからだ。

そこでファイルやクラスには、全体像に関するコメントを書くと良い。

// (例)
// このファイルには、ファイルシステムに関する便利なインタフェースを提供
// するヘルパー関数が含まれています。ファイルのパーミッションなどを扱います。

要約コメント

関数の内部でも、「全体像」についてコメントするのは良い考えだ。

# 顧客が自分で購入した商品を検索する
for customer_id in all_customers:
  for sale in all_sales[customer_id].sales:
    if sale.recipient == customer_id:
      ...

しかし、コメントがないとコードの途中で意味がわからなくなる(「all_customers」をイテレートするのは、何のために?)

以下のように関数の処理をまとめれば、詳細を調べなくても関数の概要が把握しやすい。

def GenerateUserReport():
  # このユーザーのロックを獲得する
  ...
  # ユーザーの情報をDBから読み込む
  ...
  # 情報をファイルに書き出す
  ...
  # このユーザーのロックを解放する
  ...

6章 コメントは正確で簡潔に

曖昧な代名詞を避ける

「それ」や「これ」が何を指しているのかよくわからないことがある。以下はその例。

// データをキャッシュに入れる。ただし、先にそのサイズをチェックする。

「その」が指しているのは「データ」かもしれないし「キャッシュ」かもしれない。
紛らわしいのであれば、名詞を代名詞に代入してみるといい。

// データをキャッシュに入れる。ただし、先にデータのサイズをチェックする。

あるいは、文章全体を書き換えて「それ」を明確にすることもできる。

// データが十分に小さければ、それをキャッシュに入れる。

関数の動作を正確に記述する

ファイルの行数を数える関数を書いているとする。

// このファイルに含まれる行数を返す
int Countlines(string filename){ ... }

「行」と言ってもさまざまな意味がある。

  • ""(空のファイル)は、0行なのか1行なのか
  • "hello"は、0行なのか1行なのか
  • "hello¥n"は、1行なのか2行なのか
  • "hello¥n world"は、1行なのか2行なのか

最も単純な実装は、改行文字(¥n)を数えるものだ。
この実装に適したコメントは、以下のようになる。

// このファイルに含まれる改行文字('¥n')を数える。
int Countlines(string filename){ ... }

これで改行文字がない場合は、0を返すことがわかる。

コードの意図を書く

以下のようなコードは、コードの動作をそのまま書いてるだけで何も情報を追加していない。

void DisplayProducts(list<Product> products) {
  products.sort(CompareProductByPrice);

  // listを逆順にイテレートする
  for(list<Product>::reverse_iterator it = products.rebigin(); 
      it != products.rend(); ++it;)
      DisplayPrice(it->price);
      ...
}

このコードは直下のコードをそのまま説明しているだけなので、以下のように変えてみる。

  // 値段の高い順に表示する
  for(list<Product>::reverse_iterator it = products.rebigin(); ...)

このプログラムにはバグがあり、CompareProductByPrice関数が、すでに値段の高い順にソートしている。したがって、このコードは作成者の意図に反したものになっている。
そのため、後者のコメントのほうが優れていることになる。

前者のコメントは技術的には正しい(=逆順にイテレートする)。しかし、後者のコメントのほうが作成者の意図(=値段の高い順に表示する)が、実際のコードと矛盾していることに気づきやすい。

まとめ

  • 変数名などには、明確で適切な長さの名前を入れる
  • 最善な名前とは、誤解されない名前である
  • 一貫性を持つことで、より美しいコードが書ける
  • コメントを書くことは、作成者の意図を知らせることにもつながる

参考記事

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

12
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?