Snack只有三种类型,不允许出现其它类型:
package com.lugew.springbootddd;
import lombok.Getter;
import lombok.Setter;
/**
* @author 夏露桂
* @since 2021/6/16 10:45
*/
@Getter
@Setter
public class Snack extends AggregateRoot {
public static Snack None = new Snack(0, "None");
public static Snack Chocolate = new Snack(1, "Chocolate");
public static Snack Soda = new Snack(2, "Soda");
public static Snack Gum = new Snack(3, "Gum");
private String name;
private Snack(long id, String name) {
this.id = id;
this.name = name;
}
public SnackDto convertToSnackDto() {
SnackDto snackDto = new SnackDto();
snackDto.setId(id);
snackDto.setName(name);
return snackDto;
}
}
随后将测试中的new Snack(“Some snack”) 替换为Chocolate。
package com.lugew.springbootddd.snackmachine;
import com.lugew.springbootddd.Snack;
import com.lugew.springbootddd.SnackPile;
import org.junit.jupiter.api.Test;
import static com.lugew.springbootddd.snackmachine.Money.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* @author 夏露桂
* @since 2021/6/9 18:12
*/
public class SnackMachineTest {
@Test
public void return_money_empties_money_in_transaction() {
SnackMachine snackMachine = new SnackMachine();
snackMachine.insertMoney(Dollar);
snackMachine.returnMoney();
assertEquals(snackMachine.getMoneyInTransaction(), 0, 0);
}
@Test
public void inserted_money_goes_to_money_in_transaction() {
SnackMachine snackMachine = new SnackMachine();
snackMachine.insertMoney(Cent);
snackMachine.insertMoney(Dollar);
assertEquals(snackMachine.getMoneyInTransaction(), 1.01f, 0);
}
@Test
public void cannot_insert_more_than_one_coin_or_note_at_a_time() {
SnackMachine snackMachine = new SnackMachine();
Money twoCent = Money.add(Cent, Cent);
assertThrows(IllegalStateException.class, () -> {
snackMachine.insertMoney(twoCent);
});
}
@Test
public void buySnack_trades_inserted_money_for_a_snack() {
SnackMachine snackMachine = new SnackMachine();
snackMachine.loadSnacks(1, new SnackPile(Snack.Chocolate, 10, 1));
snackMachine.insertMoney(Dollar);
snackMachine.buySnack(1);
assertEquals(snackMachine.getMoneyInTransaction(), Money.None.getAmount());
assertEquals(snackMachine.getMoneyInside().getAmount(), 1, 0.5);
assertEquals(snackMachine.getSnackPile(1).getQuantity(), 9);
}
@Test
public void cannot_make_purchase_when_there_is_no_snacks() {
SnackMachine snackMachine = new SnackMachine();
assertThrows(IllegalStateException.class, () -> {
snackMachine.buySnack(1);
});
}
@Test
public void cannot_make_purchase_if_not_enough_money_inserted() {
SnackMachine snackMachine = new SnackMachine();
snackMachine.loadSnacks(1, new SnackPile(Snack.Chocolate, 1, 2));
snackMachine.insertMoney(Dollar);
assertThrows(IllegalStateException.class, () -> {
snackMachine.buySnack(1);
});
}
@Test
public void snack_machine_returns_money_with_highest_denomination_first() {
SnackMachine snackMachine = new SnackMachine();
snackMachine.loadMoney(Dollar);
snackMachine.insertMoney(Quarter);
snackMachine.insertMoney(Quarter);
snackMachine.insertMoney(Quarter);
snackMachine.insertMoney(Quarter);
snackMachine.returnMoney();
assertEquals(snackMachine.getMoneyInside().getQuarterCount(), 4);
assertEquals(snackMachine.getMoneyInside().getOneDollarCount(), 0);
}
@Test
public void after_purchase_change_is_returned() {
SnackMachine snackMachine = new SnackMachine();
snackMachine.loadSnacks(1, new SnackPile(Snack.Chocolate, 1,
0.5f));
snackMachine.loadMoney(
new Money(0, 10, 0, 0, 0, 0)
);
snackMachine.insertMoney(Dollar);
snackMachine.buySnack(1);
assertEquals(snackMachine.getMoneyInside().getAmount(), 1.5, 0);
assertEquals(snackMachine.getMoneyInTransaction(), 0, 0);
}
@Test
public void cannot_buy_snack_if_not_enough_change() {
SnackMachine snackMachine = new SnackMachine();
snackMachine.loadSnacks(1, new SnackPile(Snack.Chocolate, 1, 0.5f));
snackMachine.insertMoney(Dollar);
assertThrows(IllegalStateException.class, () -> {
snackMachine.buySnack(1);
});
}
}
此外Slot构造器也可以优化:
public class Slot extends Entity {
public Slot(SnackMachine snackMachine, int position) {
this.snackMachine = snackMachine;
this.position = position;
this.snackPile = new SnackPile(Snack.None, 0, 0);
}
}
再次重构:
public class SnackPile extends ValueObject<SnackPile> {
public static SnackPile Empty = new SnackPile(Snack.None, 0, 0f);
}
最终结果:
package com.lugew.springbootddd;
import com.lugew.springbootddd.snackmachine.SnackMachine;
import lombok.Getter;
import lombok.Setter;
/**
* @author 夏露桂
* @since 2021/6/16 10:47
*/
@Getter
@Setter
public class Slot extends Entity {
private SnackPile snackPile;
private SnackMachine snackMachine;
private int position;
public Slot() {
}
public Slot(SnackMachine snackMachine, int position) {
this.snackMachine = snackMachine;
this.position = position;
this.snackPile = SnackPile.Empty;
}
public SlotDto convertToSlotDto() {
SlotDto slotDto = new SlotDto();
slotDto.setId(id);
slotDto.setPosition(position);
slotDto.setPrice(snackPile.getPrice());
slotDto.setQuantity(snackPile.getQuantity());
slotDto.setSnackDto(snackPile.getSnack().convertToSnackDto());
return slotDto;
}
public SnackPile getSnackPile() {
return snackPile;
}
public void setSnackPile(SnackPile snackPile) {
this.snackPile = snackPile;
}
public SnackMachine getSnackMachine() {
return snackMachine;
}
public void setSnackMachine(SnackMachine snackMachine) {
this.snackMachine = snackMachine;
}
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
}
👆持续迭代在DDD中十分常见。
Snack类型的校验可在集成测试中验证。