前置き
書籍を読んだり、ググったりして、自分に分かりやすいようにまとめた記事です。
より詳しく知りたい方は、下記の参考文献を読んでみてください。
参考文献
達人プログラマー ―熟達に向けたあなたの旅― 第2版 | Amazon
あなたはDRY原則を誤認している? - Qiita
本当の問題にたどり着かない――思考の重複を起こさないための「DRY」原則:問題解決力を高めるコツはプログラミングの原則・思考にあり - @IT
DRY原則(Don't Repeat Your Self) とは
- 一言でいうと知識を二箇所以上に重複させるな
- 同じロジックが複数箇所で使われるなら、コピペは厳禁
- 同じようなコードが複数あると、修正する際に修正漏れする危険性がある
- 同じようなコードだと思って、修正不要な箇所に誤って手を加えることになる可能性もある
- 誤解しがちなところは**「コードを重複させるな」ではない** - 「知識」や「意図」の二重化についての原則 - 異なった場所に同じことを表現するという問題を避けるための - コードだけでなく、データベーススキーマ、テスト計画、ビルドシステムやドキュメントなども含まれる
コードの二重化
商品情報から消費税を計算したり、値引した価格を求めたりするコード例
Product.cs
public class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
PriceCalculation.cs
public class PriceCalculation
{
/// <summary>
/// 商品の10%を求める
/// </summary>
/// <param name="product"></param>
/// <returns></returns>
public int GetTenPercentPrice(Product product)
{
return product.Price * 0.10;
}
/// <summary>
/// 商品の消費税を求める
/// </summary>
/// <param name="product"></param>
/// <returns></returns>
public int GetConsumptionTax(Product product)
{
return product.Price * 0.10;
}
}
改善方法
-
GetTenPercentPrice
とGetConsumptionTax
のどちらも計算内容は同じ = 共通化できる-
Product.cs
の内容は同じなので割愛
-
PriceCalculation.cs
public class PriceCalculation
{
/// <summary>
/// 商品の10%を求める
/// </summary>
/// <param name="product"></param>
/// <returns></returns>
public int GetTenPercentPrice(Product product)
{
return product.Price * 0.10;
}
}
本当にこれで良いの??
- 消費税が10%から8%に変更されたとする
-
GetTenPercentPrice
を以下のように変更する可能性あり
-
PriceCalculation.cs
public class PriceCalculation
{
/// <summary>
/// 商品の8%値引きした価格(消費税)を求める
/// </summary>
/// <param name="product"></param>
/// <returns></returns>
public int GetEightPercentPrice(Product product)
{
return product.Price * 0.08;
}
}
- 消費税の計算に関する修正としては問題ないが「10%値引きした価格計算」する処理がなくなった
- 値引き計算している処理のところでコンパイルエラーや意図しない挙動になってしまう
- 共通化しすぎかも??
本当の改善方法(例)
- 「値引き価格計算」と「消費税計算」は別々に管理するほうがよいかも
- 変更する理由を明確に分離できる
DiscountCalculation.cs
public class DiscountCalculation
{
/// <summary>
/// 商品の10%を求める
/// </summary>
/// <param name="product"></param>
/// <returns></returns>
public int GetTenPercentPrice(Product product)
{
return product.Price * 0.10;
}
}
TaxCalculation.cs
public class TaxCalculation
{
/// <summary>
/// 商品の消費税を求める
/// </summary>
/// <param name="product"></param>
/// <returns></returns>
public int GetConsumptionTax(Product product)
{
return product.Price * 0.10;
}
}
コードのコメントによる二重化
コード例
Bullet.cs
// 違反例
public class Bullet : MonoBehaviour
{
/// <summary>
/// Enemyオブジェクトに衝突したときに発火する
/// GameObjectに格納している IEnemy の Component を取得
/// Enemyオブジェクトにダメージを与える
/// Component が取得できなかったら何もしないで処理を終了する
/// </summary>
/// <param name="collision"></param>
void OnCollisionEnter(Collision collision)
{
var hit = collision.gameObject;
var enemy = hit.GetComponent<IEnemy>();
if (enemy == null)
return;
enemy.ApplyDamage();
}
}
良くない点
- コメントとコードで、この関数の情報が2度繰り返されている
- コードを修正した場合、コメントを修正する必要がある
- コメント修正を忘れる可能性大
- 時間が経つと、コードとコメントが乖離していく
- コードを修正した場合、コメントを修正する必要がある
改善方法
- 変数名、関数名などを工夫することで改善可能
- 名前で意味や意図、役割を表すようにする
- コードを読むだけで分かることはコメントに書かない
データ構造の二重化
縦、横、面積のデータ構造を持つコード例
Rectangle.cs
// 違反例
public class Rectangle
{
public int Height { get; set; }
public int Width { get; set; }
public int Area { get; set; }
}
良くない点
- 縦か横の長さが変化すると面積も変わる
改善方法
-
Area()
は縦x横で面積を計算するメソッドに変更する
Rectangle.cs
// 改善例
public class Rectangle
{
public int Height { get; set; }
public int Width { get; set; }
public int Area()
{
return Height * Width;
}
}
終わりに
SOLID原則に続いてDRY原則をまとめました。
間違いや改善点あれば、ぜひ教えてください。
SOLID原則の記事は以下です。