LoginSignup
2

More than 1 year has passed since last update.

posted at

【Java】変更に強いコード - 値オブジェクト1(Value Object)

変更に強いコードを書くための考え方を学びます

変更に強いコードの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());

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
What you can do with signing up
2