今回もリファラジへの感想を書いてみよう。
今回は、重複コードをどう判断するかという話。面白いので聞いてみるといいかも。
僕も以前考えたことがある。
重複しているコードをどう判断して共通化するかしないかについて書いてみる。
僕の結論は、字面が同じ、つまりコードの重複があるないだけでは、判断できない。意図や目的が同じかどうかも関わってくる、ということ。表にまとめると以下。これが原則的な考え方だと思う。
![](https://r2.sizu.me/users/15117/post-images/4cx8vd75i1x913in5ani.png)
①から④のそれぞれのケースをみていこう。この表からしてもう字面関係ないじゃんって気になるけど、一応流れに沿って説明する。
①字面が同じで意図が同じケース
これはわかりやすいケース。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の実装も変わってしまう。
④字面も異なるし意図も異なるケース
そもそも問題外で考える必要もないケース。絶対に共通化してはいけないもの。
---
というわけで先に示した表を見たらわかるが、字面が同じかどうかというより、意図や目的が同じかどうかで判断するとよい。というか、「字面が同じなら共通化だ!」だと思い込んでると、事故る可能性あるので要注意。