302
343

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

リーダブルコードを読んで重要だと感じたルールを抜粋

Last updated at Posted at 2023-08-28

はじめに

業務で開発をしていて、Pull Requestを送るたびに命名について厳しいレビューをもらうので、業務で特に重要だと感じた部分のみまとめてみました!

最初は「動けばいいじゃん!」と思っていたのですが、チーム開発、仕事となるとそうはいきません。
品質も含めて評価されるため、読みやすいコードを書くということは非常に重要です。

レビューで毎回のように 「ちゃんとリーダブルコードを読みましたか?」 と厳しい指摘を受けるので、できるだけその回数を減らしていきたいです。

毎日レビューで厳しい指摘を受けるのは(おそらく上司も仕事のためとしてコードに対しての指摘をしていると思われるが)とても辛いです。

レビューは あくまでもコードの指摘をしているだけ で、自分自身の人間性や仕事に対するダメ出しをもらっているということではない!と思うようにしてます。

とはいえできるだけレビューで受ける指摘は減らしたいので、ここにまとめます。

2章 命名規則

明確な単語を選ぶ

getは明確ではない。どこからとってくるのかによって動詞を使い分ける

  1. データベースから取得: fetch、retrieve

    • 例: fetchUserById, retrieveOrderDetails
  2. 外部APIから取得: call、request

    • 例: callWeatherAPI, requestTokenFromServer
  3. キャッシュから取得: loadFromCache, pullFromCache

    • 例: loadProfileFromCache, pullLatestDataFromCache
  4. データ構造やオブジェクトから取得: extract, access

    • 例: extractKeyValue, accessNestedProperty
  5. 計算や変換を伴う取得: compute, calculate, determine

    • 例: computeAverageScore, calculateTotalPrice, determineUserStatus
  6. 一時的なストレージやセッションからの取得: read, lookup

    • 例: readFromSession, lookupInTemporaryStorage
  7. 設定や設定ファイルから取得: load, read

    • 例: loadConfigurations, readSettings
  8. 状態や属性に基づく取得: check, inspect, view

    • 例: checkPermissionStatus, inspectObjectProperties, viewCurrentMode

汎用的な名前を避ける

  • tmpやretvalなどの汎用的な名前を避ける
  • 汎用的な名前を使う時はそれ相応の理由がある時のみにする
ng例
def calculate(data):
    tmp = data[0] * 0.5
    retval = data[1] + tmp
    return retval
良い例
def calculate_total_price(prices):
    half_price_first_item = prices[0] * 0.5
    total_price = prices[1] + half_price_first_item
    return total_price

抽象的な名前よりも具体的な名前を使う

具体例

  1. 変数名の例

    • 抽象的な名前: temp
    • 具体的な名前: temperatureInCelsius
      temp は一般的で多くのコンテキストで使用される短縮形ですが、これだけでは何の "temp" なのか分かりません。一方、temperatureInCelsius は摂氏の温度を示す変数であることが明確です。
  2. 関数名の例

    • 抽象的な名前: process()
    • 具体的な名前: convertJsonToXml()
      process() という名前の関数は、何を「処理」しているのか不明確です。対照的に、convertJsonToXml() はJSONをXMLに変換する機能を持つ関数であることが明確です。
  3. クラス名の例

    • 抽象的な名前: Manager
    • 具体的な名前: EmployeeDatabaseManager
      Manager は非常に幅広い意味を持つため、何を「管理」しているのかが不明確です。しかし、EmployeeDatabaseManager は従業員のデータベースを管理するクラスであることがはっきりと分かります。

接尾辞や接頭辞を使って情報を追加する

  • 単位や属性などの情報も追加し、より具体的にする
  1. 単位を接尾辞として追加
# 不明瞭な命名
length = 5
time = 3

# 単位を接尾辞として追加
length_cm = 5
time_seconds = 3
  1. 属性を接頭辞として追加
// 不明瞭な命名
let userId = '123456';
let tempId = 'abcdef';

// 接頭辞を使って属性情報を追加
let permanentUserId = '123456';
let temporaryUserId = 'abcdef';

名前の長さを決める

  • 名前が長いことは問題ではない
  • スコープの大きな変数には具体的な長い名前をつける

名前のフォーマットで情報を伝える

  • クラス名、メソッド名、メンバ変数、インスタンス、ローカル変数などでルールを定義する
  • 例えばクラスのメンバ変数にアンダースコアをつけてローカル変数と区別する

クラス名
  • クラス名は大文字開始のCamelCase(キャメルケース)を使用します。
  • 名詞や名詞句を使用して具体的に命名することをおすすめします。
class UserAccount:
    pass
メソッド名
  • メソッド名は小文字開始のcamelCaseを使用します。
  • 動詞や動詞句を使用して、そのメソッドが何をするのかを明確に示します。
def calculateTotalPrice():
    pass
メンバ変数
  • クラスのメンバ変数の前にアンダースコア(_)をつけることで、これがクラスのメンバ変数であることを示すことが一般的です。
class SampleClass:
    def __init__(self):
        self._memberVariable = 0
インスタンス
  • インスタンス変数は小文字開始のcamelCaseまたはスネークケースを使用します。
user_account = UserAccount()
ローカル変数
  • ローカル変数も小文字開始のcamelCaseまたはスネークケースを使用します。メンバ変数と区別するためにアンダースコアを前につける必要はありません。
def someFunction():
    local_variable = "This is a local variable"

3章 誤解されない名前

名前が「他の意味と間違えられないか?」を考える

避けるべき名前

  • filter()

  • clip()

  • 限界値を含める時はmin,max

  • 範囲を指定する時はfirst,last

  • ブール値は接頭辞にis,has,can,shouldをつける

  • 複数の名前を検討する

4章 美しさ

  • 一貫性のあるパターンとレイアウトにする
  • 関連するコードはまとめてブロックにする
  • 重複を排除する
    • 基本3回以上重複が出てきたらメソッドとしてまとめる
    • テストコードもメソッドとしてまとめる
  • 縦の線をまっすぐにして整列させる
  • 宣言をブロックにまとめる
  • 意味のある順序にする

5章 コメントすべきこと、すべきでないこと

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

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

  • コードをみてわかることは書かない
    • 新しい情報を提供しないコメントは書かない
# xを1増加
x += 1

この例では、コメントは冗長であり不要です。コードを見ればxを1増加させていることは明白です。

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

# TODO: この部分は効率が悪いので、後でリファクタリングする
def calculate_total(items):
    total = 0
    for item in items:
        total += item.price
    return total

このTODOコメントは、コードに存在する欠陥や問題点を示しています。

その他例
// TODO:
// maybe-later: (後で直す)
// こうすべきだがこういう理由からこう書いているなど

定数にコメントをつける

  • なぜその定数にしたかの理由をつける
# 重力加速度 (m/s^2)。この値は地球上での標準的な重力加速度として一般的に受け入れられている。
GRAVITY_ACCELERATION = 9.81

# ユーザのセッションタイムアウト時間 (秒)。この値は、ユーザビリティのテストを基に選択された。
SESSION_TIMEOUT = 1800

要約コメントをつける

  • そのクラス、関数の機能、目的が一目でわかるコメントをつける
# Userクラスは、Webアプリのユーザー情報を管理するためのクラスです。
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

# 指定されたユーザー名とEメールアドレスを使って新しいユーザーを作成する関数
def create_new_user(name, email):
    return User(name, email)

上記の例では、Userクラスとcreate_new_user関数の両方にコメントを追加して、それぞれの目的と機能を明確にしています。

6章 コメントは正確に

  • 曖昧な代名詞を避ける
    • それ、これは使わない
曖昧なコメント例
x = 10
y = 20
z = x + y
# これを50に設定する
total = z

良い例
x = 10
y = 20
z = x + y
# zの合計値をtotalに設定する
total = z

11章 一度に一つのことを

コードは一つづつタスクを行うようにしなければならない

  • タスクは小さくする
def processData(data):
    # データをクリーンアップ
    cleaned_data = cleanData(data)
    # クリーンアップしたデータを分析
    analyzed_data = analyzeData(cleaned_data)
    return analyzed_data

上記の例では、processDataという関数が一つのことをしていますが、それぞれの内部処理は明確に分けられています。まず、データのクリーンアップを行い、次にそのクリーンアップしたデータを分析します。

このようにタスクを小さくし、関数やメソッドごとに一つのことだけを行うことで、以下の利点が得られます:

  1. 可読性の向上: 各関数やメソッドが一つのタスクだけを行うため、何をしているのかをすぐに理解することができます。
  2. 再利用性: タスクが明確に分けられているため、必要に応じて他の場所でその関数やメソッドを再利用することが容易になります。
  3. テストのしやすさ: 一つの関数が一つのタスクだけを担当するため、その関数のテストを書きやすくなります。

まとめ

以上になります

もし参考になったらいいね、Twitterのフォローもお願いします!

毎日技術や個人開発、仕事に関してのツイートをしています

302
343
5

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
302
343

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?