字面が同じかどうかより 意図(目的)が同じかどうかで 共通化する

今回もリファラジへの感想を書いてみよう。

今回は、重複コードをどう判断するかという話。面白いので聞いてみるといいかも。

僕も以前考えたことがある。

重複しているコードをどう判断して共通化するかしないかについて書いてみる。

僕の結論は、字面が同じ、つまりコードの重複があるないだけでは、判断できない。意図や目的が同じかどうかも関わってくる、ということ。表にまとめると以下。これが原則的な考え方だと思う。

①から④のそれぞれのケースをみていこう。この表からしてもう字面関係ないじゃんって気になるけど、一応流れに沿って説明する。

①字面が同じで意図が同じケース

これはわかりやすいケース。DRY原則の説明でよく使われる例かもしれない。

let numbers = vec![1, 2, 3, 4, 5];

let even_squares1: Vec<i32> = numbers.iter()

.filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }).collect();

// 別のところで

let even_squares2: Vec<i32> = numbers.iter()

.filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }).collect();

字面と意図が同じなので、特に理由がなければ、関数として共通化したほうがよい。

fn even_squares(numbers: &[i32]) -> Vec<i32> {

numbers.iter()

.filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None }).collect()

}

②字面が異なるが意図が同じケース

以下のような合計値を求めるケース

let numbers = vec![1, 2, 3, 4, 5];

let total: i32 = numbers.iter().fold(0, |acc, &x| acc + x);

一方で、他ところでは目的は同じだがコード表現が異なる

let numbers = vec![1, 2, 3, 4, 5];

let mut total: i32 = 0;

for e in numbers {

total += e;

}

どちらかの実装に統一することが多いだろう。

fn sum(numbers: &[i32]) -> i32 {

numbers.iter().fold(0, |acc, &x| acc + x)

// より簡潔な numbers.iter().sum() でよさそう

}

③字面が同じで意図が異なるケース

字面が同じだからといって、共通化すると痛い目にあうことがある。それがこのケース。まぁ考えたら分かると思うが念のため。

ゲーム内の攻撃力を計算するコード例

// 特定の地形で攻撃力が2倍になる

let adjusted_attach_point: Vec<i32> = weapon_attach_points.iter()

.map(|&x| x * 2)

.filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None })

.collect();

ゲーム内の武器加工費用を計算するコード例

// 武器の特殊加工の際の費用計算

let weighted_amounts: Vec<i32> = amounts.iter()

.map(|&x| x * 2)

.filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None })

.collect();

この二つの計算は同じコードだが、意図が違うため共通化できない。たまたま字面が同じというだけだ。以下のように共通化するのは明らかに間違いだ。

fn adjusted_attach_points(values: &[i32]) -> Vec<i32> {

values.iter()

.map(|&x| x * 2)

.filter_map(|&x| if x % 2 == 0 { Some(x * x) } else { None })

.collect()

}

let adjusted_attach_points = adjusted_attach_points(&weapon_attach_points);

let weighted_amounts = adjusted_attach_points(&weighted_amounts);

adjusted_attach_pointsに仕様変更が入ったときに、意図せずにweighted_amountsの実装も変わってしまう。

④字面も異なるし意図も異なるケース

そもそも問題外で考える必要もないケース。絶対に共通化してはいけないもの。

---

というわけで先に示した表を見たらわかるが、字面が同じかどうかというより、意図や目的が同じかどうかで判断するとよい。というか、「字面が同じなら共通化だ!」だと思い込んでると、事故る可能性あるので要注意。

@j5ik2o
歳をとっても霜降り肉!をモットーにしております