『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;
}
}
@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;
}
}
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);
}
}
@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;
}
}
@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);
}
}
@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;
}
}