Geb's Moon Logo

Geb's Lab

「変数の値を推測しやすくする」と読みやすいプログラムになる

6/20/2024

良いコードとは「他の人が読みやすいコード」であると広く言われています。 ()

しかし、コードを書いているその時に、「他の人」の目線に立って「最短時間で理解できる」かを判断するのはなかなか難しいです。 4-5

「もう少し具体的な原則が欲しいな」と考えてコードを書き続けて、最近になって一つ良い原則に辿り着きました。

それは「変数の値を推測しやすくする」という原則です。 2 ...使

どういうことかイメージを掴んでもらうために、まずは具体的な方法を紹介します。

具体例

具体例を通して、方法論の重要度や優先度がどう整理できるのか、説明します。

「変数の値を推測しやすくする」一番わかりやすい方法は「変数に適切な名前をつける」です。

ただし、何でもかんでもわかりやすい名前をつければ良いというものでもないです。

const soldOutProducts = [
  { name: "A", price: 100 },
  { name: "B", price: 200 },
  { name: "C", price: 300 },
]
 
// (1)
soldOutProducts.map((soldOutProduct) => {
  return {
    ...soldOutProduct,
    price: soldOutProduct.price * 1.1,
  }
})
 
// (2)
soldOutProducts.map((p) => {
  return {
    ...p,
    price: p.price * 1.1,
  }
})

この場合、map内のコールバックの引数に適切な名前が付いていない後者の方が、私は読みやすいです。

この名前を正確にすべきか短くすべきか問題は、「変数の値を推測しやすくする」という原則に基づくと判断できます。

つまり、「基本は正確な名前をつけるべきだが、その変数の値を代入してすぐ利用するなら、値を追えるから名前はなんでも良い」ということです。 pmapx3p

この例のように、「変数の値を推測しやすくする」という判断基準に沿うと、良いコードを書くテクニックをいつ・どう使えば良いかが分かります。

では、なぜ「変数の値を推測しやすくする」と「読みやすいコード」になるのでしょうか?

なぜ「変数の値を推測しやすくする」ことが重要なのか

「変数の値を推測しやすくする」と「読みやすいコード」になるのは、「コードを読む」という行為が、「使われている変数の値を頭の中で追跡する」こととほぼイコールだからです。 ()

したがって、「頭の中で変数の値を推測しやすい」イコール「読みやすい」になるのです。

それでは、「変数の値を推測しやすくする」ための具体的な方法を見ていきましょう。 使

実践

変数の値を推測しやすくするには、まず可能な限り「推測すべき変数を減らす」ようにし、残った変数に対して「変数の値を分かりやすくする」というアプローチを取ります。

その方法を順にご紹介します。

変数の数を減らす

変数がなくなれば、その変数の値を推測する必要がなくなります。

ソースコードをなるべく減らして、余計な変数やロジックを削除しましょう。 1 JavaScriptmapなど、Pythonならリスト内包表記など、変数を減らせる記法を覚えて使いましょう。

  function doubleNumbers(numbers) {
-   let doubledNumbers = [];
-   for (let i = 0; i < numbers.length; i++) {
-       doubledNumbers.push(numbers[i] * 2);
-   }
-   return doubledNumbers;
+   return numbers.map(num => num * 2);
  }

どうしても必要な変数だけを残したら、次の方法を使います。

変数の利用範囲(スコープ)を狭める

プログラム全体での変数の数が減らせなくても、変数の利用範囲をなるべく狭めれば、ある部分のコードを読むときに予測すべき変数の値の数は少なくなります。

スコープを狭めるには、基本的にはコードを関数に切り出します。

  let users = [
    { name: 'Alice Johnson', age: 28, email: '[email protected]' },
    { name: 'Bob Smith', age: 17, email: '[email protected]' },
    { name: 'Charlie Brown', age: 22, email: '[email protected]' },
    { name: 'David Miller', age: 15, email: '[email protected]' },
    { name: 'Eve Davis', age: 35, email: '[email protected]' }
  ];
 
  let validUsers = [];
  let emails = [];
 
  for (let i = 0; i < users.length; i++) {
    let user = users[i];
    
-   let nameParts = user.name.split(' ');
-   let lastName = nameParts.length > 1 ? nameParts[1] : '';
-   let isValid = user.age >= 18 && lastName.length >= 5;
- 
-   if (isValid) {
-     validUsers.push(user);
-   }
+   if (isValidUser(user)) {
+     validUsers.push(user);
+   }
 
    if (user.email.includes('@')) {
      emails.push(user.email);
    }
  }
 
  console.log('Valid Users:', validUsers);
  console.log('Emails:', emails);
 
+ function isValidUser(user) {
+   let nameParts = user.name.split(' ');
+   let lastName = nameParts.length > 1 ? nameParts[1] : '';
+   return user.age >= 18 && lastName.length >= 5;
+ }

スコープの広い変数は減らすべきで、ごく狭い変数はたくさんあっても良いです。 10

関数へ切り出すときに少し工夫をすれば、さらに変数の値を推測しやすくなります。

引数と返り値の型定義とコメントで、関数の挙動を説明する

関数に使い方をコメントすれば、関数の中にある変数の値を推測する必要がなくなります。

多くの言語で、使い方を書くための専用記法があるので、それを使いましょう。 javascriptJSDocpythondocstring

// 関数名から自明なことはコメントしないこと!
+ /**
+  * ユーザがアプリの認証済みユーザーとして有効かどうかを判定する
+  * @param user ユーザ
+  * @returns 有効ならtrue
+  */
  function isValidUser(user) {
    let nameParts = user.name.split(' ');
    let lastName = nameParts.length > 1 ? nameParts[1] : '';
    return user.age >= 18 && lastName.length >= 5;
  }

ただし、関数の名前から自明なことはコメントしないようにしましょう。 ユーザが有効かどうかを判定するというコメントは不要です。 今回の例では、「アプリの認証済みユーザーとして」有効かどうかの判定という付加情報をコメントに書いています。

もう一つ、推測すべき変数を減らす関数の実装方法として重要な点があります。

関数の出力を引数以外の変数に依存させない

引数の値が同じなら、関数は必ず同じ値を返すように実装しましょう。

引数以外の状態、例えば関数の外で定義した変数やDBの値などによって結果が変わると、その値を推測しながら関数を使う必要が生じます。 DB

推測すべき変数を減らす方法はこのくらいです。次は変数の値を推測しやすくする方法をご紹介します。

命名と型定義で変数の値を説明する

関数や変数に値を説明する名前と型定義があると、格段に変数の値を推測しやすくなります。

+ type User = {
+   name: string;
+   age: number;
+   email: string;
+ }
- let users = [
+ let users: User[] = [
    ...
  ];
 
- let validUsers = [];
+ let validUsers: User[] = [];
- let emails = [];
+ let emails: string[] = [];
  ...
+ function isValidUser(user: User): boolean {
    ...
  }

特に型定義はメリットが分かるまでサボりがちですが、非常に有効です。 VSCodeIDE IDE沿 ... & any😠

命名については前節の具体例でも示した通りです。 調

変数の値を変更しない

「変数の値がコロコロ書き換わるのは気持ち悪い、不安だ」という感覚を持ち、変数の値を変更する操作をなるべく避けましょう。

言語仕様として値を変更できない (イミュータブルな) データ型が存在することも多いですので、なるべく使いましょう。 Python rust rust

ただし、全ての変数値を変更しないようにするのは難しいですし、逆に読みにくくなることもあります。 JavaScript

可読性や言語使用上の制限によって、変数の値を変更する必要がある場合は、次のテクニックを使います。

変数の値の変更を、コードを読む人に見える場所で行う

再代入をする場合は、読み手が気付きやすい場所で行いましょう。

やりがちなのが、クラスの状態をそのメソッドの中でコロコロ変えてしまうことです。 updateXXXState など)をつけましょう。 逆にgetのようなデータを読み取るだけに見えるメソッドの中では、状態を変えないようにしましょう。


広く使えて効果が高そうな方法はこのくらいです。

ただし、大事なのはこの方法を覚えておくことよりも、「変数の値を推測しやすくする」という基準でコーディングする意識を持つことです。

コーディングに限らないことですが、技術や方法をただ覚えておくだけでは、既知の問題にしか適用できません。

未知の問題にどの技術や方法を使えば良いかは、「何のためにその技術や方法を使うのか?」を理解しておくことが不可欠です。