0
0

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.

「リーダブルコード」要約_10章

Last updated at Posted at 2022-11-27

10章 無関係の下位問題を抽出する

10章でのアドバイスは、「無関係の下位問題を積極的に見つけて抽出すること」だ。
そのためには、以下の3点を実践すれば良い。

  1. 関数やコードブロックを見て「このコードの高レベルの目標は何か?」を自問する。
  2. コードの各行に対して「高レベルの目標に直接効果はあるか」それとも
    「無関係の下位問題を解決しているか」を判断する。
  3. 無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して別の関数にする。

以下、具体的な例で解説する。

入門的な例
以下のようなJavaScriptのコードがあるとする。
このコードの高レベルの目標は「与えられた地点から最も近い地点を見つける」ことである。

// 与えらた軽度・緯度に最も近い'array'の要素を返す。
// 地球が完全な球体であることを前提としている。
var findCloseLocation = (lat, lng, array) => {
  var closest;
  var closest_dist = Number.MAX_VALUE;
  for (var i = 0; i < array.length; i += 1) {
    // 2つの地点をラジアンに変換する。
    var lat_rad = radians(lat);
    var lng_rad = radians(lng);
    var lat2_rad = radians(array[i].latitude);
    var lng2_rad = radians(array[i].longitude);

    // 「球面三角法の第二余弦定理」の公式を使う。
    var dist = Math.acos(
      Math.sin(lat_rad) * Math.sin(lat2_rad) +
      Math.cos(lat_rad) * Math.cos(lat2_rad) *
      Math.cos(lng2_rad - lng_rad));
    if (dist < closest_dist) {
      closest = array[i];
      closest_dist = dist;
    }
  }
  return closet;
};

ループ内のコードは、「2つの地点の球面距離を算出する」という下位問題を扱っている。
これを別の関数spherical_distance()として抽出すると良い。
そうすると、全体を以下のように書き直すことができる。

// 2つの地点(緯度・経度)の球面距離を算出する。
const spherical_distance = (lat1, lng1, lat2, lng2) => {
  //2つの地点をラジアンに変換する。
  var lat1_rad = radians(lat1);
  var lng1_rad = radians(lng1);
  var lat2_rad = radians(lat2);
  var lng2_rad = radians(lng2);

  // 「球面三角法の第二余弦定理」の公式を使う。
  return Math.acos(Math.sin(lat11_rad) * Math.sin(lat2_rad) +
         Math.cos(lat1_rad) * Math.cos(lat2_rad) *
         Math.cos(lng2_rad - lng1_rad));
};

const findClosestLocation = (lat, lng, arry) => {
  var closest;
  var closest_dist = Number.MAX_VALUE;
  for (var i = 0; i < array.length; i += 1) {
    var dist = spherical_distance(lat, lng,  array[i].latitude, array[i].longitude);
    if (dist < closest_dist) {
      closest = array[i];
      closest_dist = dist;
    }
  }
  return closest;
};

コードが読みやすくなり、高レベルの目標に集中できるようになった。
さらに、spherical_distance()は個別にテストができる関数であり、将来的に再利用も可能だ。

純粋なユーティリティコード
例えば、以下のような基本的なタスクの実装については、プログラミング言語側で用意されている「組み込み関数」が利用できる場合が多い。

  • 文字列の操作
  • ハッシュテーブルの使用
  • ファイルの読み書き

具体的には、ファイルの中身を全て読み込みたければ、PHPならfile_get_contents("filename")が使える、といったように。

しかし、こうした基本的なタスクであっても、それ用の組み込み関数が用意されていない場合もある。
そういったケースでは、自分で関数を書くことが望ましい。
そうして作ったコードは、複数のプロジェクトで使えるユーティリティ(便利な)コードになっていくだろう。

その他の汎用コード
以下のJavaScriptのコードでは、Ajaxでデータをサーバに送信し、デバッグのために返ってきた値を表示している。

ajax_post({
 url: 'http://XXX.com/submit',
 data: data,
 on_success: function (response_data) {
   vat str = "{\n";
   for (var key in response_data) {
       str += " " + key + "=" + response_data[key] + "\n";
   }
   alert(str + "}");
   //引き続き'response_data'の処理
 }
});

このコードの高レベルの目標は「サーバーをAjaxで呼び出してレスポンスを処理する」である。
しかし、コードの大部分は「レスポンスを見やすく整形して出力する」という「無関係の下位問題」を扱っている。
この整形処理は、format_pretty(obj)という別の関数として抽出できる。

var format_pretty = function (obj) {
 var str = "{\n";
 for (var key in obj) {
   str += " " + key + "=" + obj[key] + "\n";
 }
 return str + "}";
};

それにより、以下のようなメリットがある。

  • format_pretty()を抽出したことで、元のコードが簡潔になった。
  • format_pretty()は独立した関数のため、改善が楽で、かつ再利用も可能。

汎用コードをたくさん作る
汎用コードの素晴らしい点は、プロジェクトから完全に切り離されている点だ。
このようなコードは、開発もテストも理解も楽である。
かつ、複数のプロジェクトで再利用できるので、簡単に共有できるよう特別なディレクトリ(例:util/)を用意するのが良い。

プロジェクトに特化した機能
抽出する下位問題というのは、プロジェクトから完全に独立したものである方が良い。
ただし、完全に独立していないくても、それはそれで問題ない。
下位問題を取り除くだけでも効果がある。

10章のまとめ

  • プロジェクト固有のコードから汎用コードを分離する
  • ただし、やりすぎは良くない。小さい関数を作りすぎると逆に読みづらくなるため。
    やみくもに分離させるのではなく、再利用できる場合に分離させることが望ましい。
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?