変更に強いコードを書くための考え方を学びます
#変更に強いコードの5つのポイント
-
1. 変数名は説明になるようにつけよう
- 入力、処理、出力の役割ごとに変数名を使う(説明用の変数を導入)
-
2. 目的ごとに変数を用意しよう
- 計算した結果に対して変数を用意して、使いまわさない
-
3. コードは意味のある段落に分けよう
- 入力、処理、出力に分ける
-
4. コードのまとまり(段落)をメソッドとして独立させよう
- 異なるクラスでの重複コードはなくそう
-
5. 狭い関心ごとに特化したクラス(ドメインオブジェクト)にしよう
- 凝集度up = あるクラスはある目的にのみ存在する
- ex: 送料クラスの関心事は送料のみ
#小さなクラスでわかりやすく安全に
- 5つのポイントをおさえ、特定の関心に特化した小さなクラスを作る
- メソッドは短く、クラスは小さく
- クラスの可読性up、変更が簡単になる
- 継承で共通機能は親クラスへ
- 派生クラスでは差分のspecificな部分のみ書くことで最低限のコードにする
- **値を扱う専用のクラス(値オブジェクト)**を作る
#値オブジェクト(Value Object)とは?
- データを使った業務ロジック:判断/加工/計算
- このデータの種類ごとに、値を扱う専用のクラスを作るということ。
- 専用の型を用意(クラスやインターフェース)
- 専用の型を使うと不適切な値が混入するバグを防げる!
- 変更時にも、クラスやインターフェース名を手掛かりに変更できる
- 値オブジェクトは非常に重要
##値オブジェクト実践
- 以下のコードをリファクタする時。。🤔
int unitPrice = UNIT_PRICE;
int basePrice = unitPrice * quantity;
int basePriceWithTax = (int)(basePrice*TAX);
-
intは基本型なので使わないほうがいい
- ここで値の種類ごとに専用の型を用意!=値オブジェクト
- valuesクラスにもつint 型のvalueを保存
Project Root
└─src
└─ main
└─ java
└─ Main
└─ values
└─ Price
└─ Quantity
└─ Tax
##Price / Quantity / Tax型を作る
- 基本型ではなく専用型を用意し、intやfloatをPrice、Quantity、Taxに変更
- getter(ここではgetValue())で値を返し、外から参照できるようにする
- 処理中身はintなので計算中ではintになっているが、
- 入力/処理/出力の処理のみintで、クラスの中に閉じ込め、返すのはPrice型
- →変更をここだけに閉じ込めることができる🌟
Price.java
package values;
public class Price {
private int value;
public Price(int value){
this.value = value;
}
public Price multiple(Quantity quantity){
return new Price(this.value * quantity.getValue());
}
public Price getPriceWithTax(Tax tax){
//tax.getValueはfloatなのでCast
int priceWithTax = (int)(this.value * tax.getValue());
//新しいオブジェクトを返す
return new Price(priceWithTax);
}
public int getValue() {
return value;
}
}
Quantity.java
package values;
public class Quantity {
private int value;
public Quantity (int value){
this.value = value;
}
public int getValue() {
return value;
}
}
Tax.java
package values;
public class Tax {
private float value;
public Tax (float value){
this.value = value;
}
public float getValue() {
return value;
}
}
Main.java
import values.Price;
import values.Quantity;
import values.Tax;
public class Main {
private final static int UNIT_PRICE = 3000;
private final static float TAX = 1.1f;
public static void main(String[] args) {
//コンストラクタで初期化
var instance = new Main();
var quantity = new Quantity(3);
var totalCost = instance.getTotalCost(quantity);
System.out.println(quantity.getValue());
System.out.println("totalCost" + totalCost.getValue());
}
Price getTotalCost(Quantity quantity){
Price unitPrice = new Price(UNIT_PRICE);
Price basePrice = unitPrice.multiple(quantity);
System.out.println("basePrice: "+basePrice.getValue());
Tax tax = new Tax(TAX);
Price basePriceWithTax = basePrice.getPriceWithTax(tax);
/*リファクタ前
int unitPrice = UNIT_PRICE;
int basePrice = unitPrice * quantity;
int basePriceWithTax = (int)(basePrice*TAX);
*/
return basePriceWithTax;
}
}
##関心事を外に閉じ込められてない例
- 上のMain.javaでの操作はこんな風に書くこともできる。が。。。。
- 値オブジェクトを使いながら、実際の処理を外に書いてしまっている🙀 (= Priceクラスを使う側で処理を書いている)
- なので、せっかく値オブジェクトを使っているのに、関心事を外に閉じ込められてないということになります。。
//よくない例
Price unitPrice = new Price(UNIT_PRICE);
int basePrice = unitPrice.getValue() * quantity.getValue();
Tax tax = new Tax(TAX);
int basePriceWithTax = (int) (basePrice * tax.getValue());