LoginSignup
1
0

More than 1 year has passed since last update.

「リーダブルコード」第10章 無関係の下位問題を抽出する

Last updated at Posted at 2022-05-07

はじめに

「リーダブルコード」第10章 無関係の下位問題を抽出する

で、紹介されていた関数(findClosestLocation)をTypeScriptにしたので、そのメモです。

コード

Before

const radians = (degree: number) => degree * (Math.PI / 180);

interface Place {
  lat: number;
  lng: number;
}

// 与えられた緯度経度に最も近い 配列 "placeList" の要素を返す
const findClosestLocation = (lat: number, lng: number, placeList: Place[]) => {
  let closestDist = Number.MAX_VALUE;
  let closestPlace;
  placeList.forEach((place: Place) => {
    const latRad = radians(lat);
    const lngRad = radians(lng);
    const lat2Rad = radians(place.lat);
    const lng2Rad = radians(place.lng);
    const dist = Math.acos(
      Math.sin(latRad) * Math.sin(lat2Rad) +
        Math.cos(latRad) * Math.cos(lat2Rad) * Math.cos(lng2Rad - lngRad)
    );
    // 算出した距離が他の一番近い距離より近ければ更新
    if (dist < closestDist) {
      closestPlace = place;
      closestDist = dist;
    }
  });
  return closestPlace;
};

// ちなみに返り値は名古屋駅の緯度経度
console.log(
  // 第一第二引数は福岡駅の緯度経度
  findClosestLocation(33.590188, 130.420685, [
    { lat: 35.1706431, lng: 136.8816945 }, // 名古屋駅の緯度経度
    { lat: 35.6809591, lng: 139.7673068 } // 東京駅の緯度経度
  ])
);

Number.MAX_VALUE プロパティは、 JavaScript において表すことが可能な最大の数値です。

After(リファクタ後)

下位問題として切り出された関数(sphericalDistance)の幾何学計算は、球面余弦定理で球面上の距離を計算しています。

参考

const radians = (degree: number) => degree * (Math.PI / 180);

interface Place {
  lat: number;
  lng: number;
}

const sphericalDistance = (
  lat1: number,
  lng1: number,
  lat2: number,
  lng2: number
) => {
  const lat1Rad = radians(lat1);
  const lng1Rad = radians(lng1);
  const lat2Rad = radians(lat2);
  const lng2Rad = radians(lng2);
  const dist = Math.acos(
    Math.sin(lat1Rad) * Math.sin(lat2Rad) +
      Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.cos(lng2Rad - lng1Rad)
  );
  return dist;
};

// 与えられた緯度経度に最も近い 配列 "placeList" の要素を返す
const findClosestLocation = (lat: number, lng: number, placeList: Place[]) => {
  let closestDist = Number.MAX_VALUE;
  let closestPlace;
  placeList.forEach((place: Place) => {
    const dist = sphericalDistance(lat, lng, place.lat, place.lng);
    // 算出した距離が他の一番近い距離より近ければ更新
    if (dist < closestDist) {
      closestPlace = place;
      closestDist = dist;
    }
  });
  return closestPlace;
};

// ちなみに返り値は名古屋駅の緯度経度
console.log(
  // 第一第二引数は福岡駅の緯度経度
  findClosestLocation(33.590188, 130.420685, [
    { lat: 35.1706431, lng: 136.8816945 }, // 名古屋駅の緯度経度
    { lat: 35.6809591, lng: 139.7673068 } // 東京駅の緯度経度
  ])
);

感想

つまり、「関数に直接関係ない部分を別の関数にする」ということですね。

そうすることで、可読性が上がり、再利用もでき、単体テストもできるのでメンテナンスしやすいですね。

共通部品を作る時は、「こんな引数を受け取るのもあったら他にも別件で誰かが使うかも」と考えて作れるとより汎用性が上がるなと思いました。

無関係の下位問題を汎用的な関数として切り出して残ったのが、その機能固有の本質的なコード、上位問題です。なので、上位問題と下位問題を切り分けてコードを書き分けるようになりたい。

要約版

参考

これの第10章

1
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
1
0