Skip to content

Instantly share code, notes, and snippets.

@asufana
Last active September 12, 2016 12:41
Show Gist options
  • Save asufana/474c9f085293b296631f to your computer and use it in GitHub Desktop.
Save asufana/474c9f085293b296631f to your computer and use it in GitHub Desktop.
フロー形式での在庫管理モデル

フロー形式での在庫管理モデル

『UMLモデリングの本質』での題材(酒問屋の在庫管理)を、Javaコードでサンプル実装する。

テストコード

ビール在庫.quantity() にて在庫数をフロー形式にて導出取得する。

public class ScenarioTest extends UnitTest {
    
    private Warehouse 倉庫;
    private Item ビール, ワイン;
    private Stock ビール在庫, ワイン在庫;
    private InitialStock ビール期首在庫, ワイン期首在庫;
    
    private DateMidnight 今日, 明日;
    
    @Before
    public void before() {
        Fixtures.deleteDatabase();
        
        倉庫 = new Warehouse("倉庫").save();
        ビール = new Item("ビール").save();
        ワイン = new Item("ワイン").save();
        
        ビール在庫 = new Stock(倉庫, ビール).save();
        ワイン在庫 = new Stock(倉庫, ワイン).save();
        
        ビール期首在庫 = new InitialStock(ビール在庫, 12);
        ワイン期首在庫 = new InitialStock(ワイン在庫, 12);
        
        今日 = new DateMidnight();
        明日 = 今日.plusDays(1);
    }
    
    @Test
    public void scenario01() throws Exception {
        
        //現時点でのビール在庫が1ダースであること
        assertThat(ビール在庫.quantity(ビール期首在庫, 今日).value(), is(12));
        
        //ビールを1ダース入荷
        // -> 現時点でのビール在庫が2ダースになること
        new Deal(今日, ビール在庫, +12).save();
        assertThat(ビール在庫.quantity(ビール期首在庫, 今日).value(), is(24));
    }
}

以下エンティティと値オブジェクトのコード

クラス図

倉庫エンティティ

@Entity(name = "warehouse")
public class Warehouse extends EntityModels<Warehouse> {
    
    /** 倉庫名 */
    @Column
    private final WarehouseName name;
    
    /** コンストラクタ(overload) */
    public Warehouse(final String name) {
        this(new WarehouseName(name));
    }
    
    /** コンストラクタ */
    public Warehouse(final WarehouseName name) {
        this.name = name;
    }
    
    @Override
    public boolean isSatisfied() {
        new Valid(name).notNull();
        return true;
    }
}

倉庫.倉庫名VO

@Embeddable
public class WarehouseName extends ValueObject<WarehouseName> {
    
    @Column(name = "name", nullable = false, length = 255)
    private final String value;
    
    //コンストラクタ
    public WarehouseName(final String value) {
        this.value = value;
        validate();
    }
}

商品エンティティ

@Entity(name = "item")
public class Item extends EntityModels<Item> {
    
    /** 品名 */
    @Embedded
    private final ItemName name;
    
    /** コンストラクタ(overload) */
    public Item(final String name) {
        this(new ItemName(name));
    }
    
    /** コンストラクタ */
    public Item(final ItemName name) {
        this.name = name;
    }
    
    @Override
    public boolean isSatisfied() {
        new Valid(name).notNull();
        return true;
    } 
}

商品.商品名VO

public class ItemName extends ValueObject<ItemName> {
    
    @Column(name = "name", nullable = false, length = 255)
    private final String value;
    
    //コンストラクタ
    public ItemName(final String value) {
        this.value = value;
        validate();
    }    
}

在庫エンティティ

実際には在庫数は保持しないのでクラス名は変更したほうがいいかもしれない。

@Entity(name = "stock")
public class Stock extends EntityModels<Stock> {
    
    /** 倉庫 */
    @ManyToOne(fetch = FetchType.LAZY)
    private final Warehouse warehouse;
    
    /** 商品 */
    @ManyToOne(fetch = FetchType.LAZY)
    private final Item item;
    
    /** コンストラクタ */
    public Stock(final Warehouse warehouse, final Item item) {
        this.warehouse = warehouse;
        this.item = item;
    }
    
    @Override
    public boolean isSatisfied() {
        new Valid(warehouse).notNull();
        new Valid(item).notNull();
        return true;
    }
    
    /** 指定日の在庫 */
    public StockQuantity quantity(final InitialStock initialStock,
                                  final DateMidnight date) {
        return StockCalc.calc(initialStock, date);
    }
}

在庫数計算

public class StockCalc {
    
    /** 指定日の在庫 */
    public static StockQuantity calc(final InitialStock initialStock,
                                     final DateMidnight date) {
        //期首在庫設定日以後の指定在庫の取引一覧を取得
        final DealCollection deals = initialStock.deals();
        //期首在庫設定日以後の数量合計を取得
        final DealQuantity dealQuantity = deals.quantity();
        //期首在庫と合算
        return initialStock.addQuantity(dealQuantity);
    }
}

期首在庫エンティティ

@Entity(name = "initial_stock")
public class InitialStock extends EntityModels<InitialStock> {
    
    /** 在庫 */
    @ManyToOne(fetch = FetchType.LAZY)
    private final Stock stock;
    
    /** 在庫数 */
    @Column
    private final InitialQuantity quantity;
    
    /** コンストラクタ(overload) */
    public InitialStock(final Stock stock, final Integer quantity) {
        this(stock, new InitialQuantity(quantity));
    }
    
    /** コンストラクタ */
    public InitialStock(final Stock stock, final InitialQuantity quantity) {
        this.stock = stock;
        this.quantity = quantity;
    }
    
    @Override
    public boolean isSatisfied() {
        new Valid(stock).notNull();
        new Valid(quantity).notNull();
        return true;
    }
    
    /** 期首在庫設定日以後の取引一覧取得 */
    public DealCollection deals() {
        //TODO ほんとはちゃんと期首在庫設定日を見ること
        final List<Deal> deals = Deal.<Deal> findAll();
        return new DealCollection(deals);
    }
    
    /** 取引数との加算 */
    public StockQuantity addQuantity(final DealQuantity dealQuantity) {
        return quantity.add(dealQuantity);
    } 
}

期首在庫.期首在庫数VO

@Embeddable
public class InitialQuantity extends ValueObject<InitialQuantity> {
    
    @Column(name = "initial_quantity", nullable = false)
    private final Integer value;
    
    //コンストラクタ
    public InitialQuantity(final Integer value) {
        this.value = value;
        validate();
    }
    
    /** 取引数との加算 */
    public StockQuantity add(final DealQuantity dealQuantity) {
        return new StockQuantity(value + dealQuantity.value());
    }
}

取引エンティティ

/** 取引エンティティ */
@Entity(name = "deal")
public class Deal extends EntityModels<Deal> {
    
    /** 取引日 */
    @Column
    private final DealDate date;
    
    /** 在庫 */
    @ManyToOne(fetch = FetchType.LAZY)
    private final Stock stock;
    
    /** 取引数 */
    @Column
    private final DealQuantity quantity;
    
    /** コンストラクタ(overload) */
    public Deal(final DateMidnight date,
            final Stock stock,
            final Integer quantity) {
        this(new DealDate(date), stock, new DealQuantity(quantity));
    }
    
    /** コンストラクタ */
    public Deal(final DealDate date,
            final Stock stock,
            final DealQuantity quantity) {
        this.date = date;
        this.stock = stock;
        this.quantity = quantity;
    }
    
    @Override
    public boolean isSatisfied() {
        new Valid(stock).notNull();
        new Valid(quantity).notNull();
        new Valid(date).notNull();
        return true;
    }
    
    /** 取引数 */
    public DealQuantity quantity() {
        return quantity;
    }
}

取引.取引数VO

@Embeddable
public class DealQuantity extends ValueObject<DealQuantity> {
    
    @Column(name = "deal_quantity", nullable = false)
    private final Integer value;
    
    //コンストラクタ
    public DealQuantity(final Integer value) {
        this.value = value;
        validate();
    }
    
    /** 値 */
    public Integer value() {
        return value;
    }
    
    /** 加算 */
    public DealQuantity add(final DealQuantity other) {
        return new DealQuantity(value + other.value);
    }
}

取引.取引日VO

@Embeddable
public class DealDate extends ValueObject<DealDate> {
    
    @Column(name = "deal_date", nullable = true)
    @Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
    private final DateTime value;
    
    //コンストラクタ
    public DealDate(final DateMidnight value) {
        this.value = value.toDateTime();
        new Valid(value).notNull();
    }
}

取引リスト

public class DealCollection extends AbstractCollection<DealCollection, Deal> {
    
    /** コンストラクタ */
    public DealCollection(final List<Deal> list) {
        super(list);
    }
    
    @Override
    protected AbstractCollection constructor(final List<Deal> list) {
        return new DealCollection(list);
    }
    
    /** 取引数量の合計 */
    public DealQuantity quantity() {
        DealQuantity quantity = new DealQuantity(0);
        for (final Deal deal : list()) {
            quantity = quantity.add(deal.quantity());
        }
        return quantity;
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment