先看看结构图:
    image.png
    为了实现购买功能,需要增加两个新实体:Snack零食和Slot零食槽
    image.png
    Snack类只有name字段,Slot实体链接Snack和SnackMachine,同时也包含自身信息:位置,价格和数量。

    1. package com.lugew.springbootddd;
    2. import lombok.Getter;
    3. import lombok.Setter;
    4. /**
    5. * @author 夏露桂
    6. * @since 2021/6/16 10:45
    7. */
    8. @Getter
    9. @Setter
    10. public class Snack extends Entity {
    11. private String name;
    12. public Snack() {
    13. }
    14. public Snack(String name) {
    15. this.name = name;
    16. }
    17. }

    显然,Snack是实体。另一个实体是Slot:

    1. package com.lugew.springbootddd;
    2. import com.lugew.springbootddd.snackmachine.SnackMachine;
    3. import lombok.Getter;
    4. import lombok.Setter;
    5. /**
    6. * @author 夏露桂
    7. * @since 2021/6/16 10:47
    8. */
    9. @Getter
    10. @Setter
    11. public class Slot extends Entity {
    12. private Snack snack;
    13. private int quantity;
    14. private float price;
    15. private SnackMachine snackMachine;
    16. private int position;
    17. public Slot() {
    18. }
    19. public Slot(SnackMachine snackMachine, int position, Snack snack, int quantity,
    20. float price) {
    21. this.snackMachine = snackMachine;
    22. this.position = position;
    23. this.snack = snack;
    24. this.quantity = quantity;
    25. this.price = price;
    26. }
    27. }

    此时,重构测试方法money_in_transaction_goes_to_money_inside_after_purchase至buySnack_trades_inserted_money_for_a_snack:

    1. @Test
    2. public void buySnack_trades_inserted_money_for_a_snack() {
    3. SnackMachine snackMachine = new SnackMachine();
    4. snackMachine.loadSnacks(1, new Snack("Some snack"), 10, 1);
    5. snackMachine.insertMoney(Dollar);
    6. snackMachine.buySnack(1);
    7. assertEquals(snackMachine.getMoneyInTransaction(),
    8. 0);
    9. assertEquals(snackMachine.getMoneyInside().getAmount(), 1, 0.5);
    10. Slot slot = snackMachine.getSlots().stream().filter(x -> x.getPosition() ==
    11. 1).findAny().orElse(null);
    12. assertEquals(slot.getQuantity(), 9);
    13. }

    和SnackMachine:

    1. package com.lugew.springbootddd.snackmachine;
    2. import com.lugew.springbootddd.Entity;
    3. import com.lugew.springbootddd.Slot;
    4. import com.lugew.springbootddd.Snack;
    5. import com.lugew.springbootddd.SnackMachineDto;
    6. import lombok.Getter;
    7. import lombok.Setter;
    8. import java.util.Arrays;
    9. import java.util.List;
    10. import static com.lugew.springbootddd.snackmachine.Money.None;
    11. @Getter
    12. @Setter
    13. public final class SnackMachine extends Entity {
    14. private Money moneyInside;
    15. private Money moneyInTransaction;
    16. private List<Slot> slots;
    17. public SnackMachine() {
    18. moneyInside = None;
    19. moneyInTransaction = None;
    20. slots = new ArrayList<>();
    21. slots.add(new Slot(this, 1, null, 0, 1));
    22. slots.add(new Slot(this, 2, null, 0, 1));
    23. slots.add(new Slot(this, 3, null, 0, 1));
    24. }
    25. public void insertMoney(Money money) {
    26. Money[] coinsAndNotes = {Money.Cent, Money.TenCent, Money.Quarter,
    27. Money.Dollar, Money.FiveDollar,
    28. Money.TwentyDollar};
    29. if (!Arrays.asList(coinsAndNotes).contains(money))
    30. throw new IllegalStateException();
    31. moneyInTransaction = Money.add(moneyInTransaction, money);
    32. }
    33. public void returnMoney() {
    34. moneyInTransaction = None;
    35. }
    36. public void buySnack(int position) {
    37. Slot slot = slots.stream().filter(x -> x.getPosition() ==
    38. position).findAny().orElse(null);
    39. slot.setQuantity(slot.getQuantity() - 1);
    40. moneyInside = Money.add(moneyInside, moneyInTransaction);
    41. moneyInTransaction = None;
    42. }
    43. public SnackMachineDto convertToSnackMachineDto() {
    44. SnackMachineDto snackMachineDto = new SnackMachineDto();
    45. snackMachineDto.setId(id);
    46. snackMachineDto.setMoneyInTransaction(moneyInTransaction.getAmount());
    47. snackMachineDto.setOneCentCount(moneyInside.getOneCentCount());
    48. snackMachineDto.setTenCentCount(moneyInside.getTenCentCount());
    49. snackMachineDto.setQuarterCount(moneyInside.getQuarterCount());
    50. snackMachineDto.setOneDollarCount(moneyInside.getOneDollarCount());
    51. snackMachineDto.setFiveDollarCount(moneyInside.getFiveDollarCount());
    52. snackMachineDto.setTwentyDollarCount(moneyInside.getTwentyDollarCount());
    53. return snackMachineDto;
    54. }
    55. public void loadSnacks(int position, Snack snack, int quantity, float price) {
    56. Slot slot = slots.stream().filter(x -> x.getPosition() ==
    57. position).findAny().orElse(null);
    58. if (slot != null) {
    59. slot.setSnack(snack);
    60. slot.setQuantity(quantity);
    61. slot.setPrice(price);
    62. }
    63. }
    64. }

    此外,还有SnackMachineController:

    1. package com.lugew.springbootddd;
    2. import com.lugew.springbootddd.snackmachine.Money;
    3. import com.lugew.springbootddd.snackmachine.SnackMachine;
    4. import lombok.RequiredArgsConstructor;
    5. import org.springframework.web.bind.annotation.*;
    6. import static com.lugew.springbootddd.snackmachine.Money.*;
    7. /**
    8. * @author 夏露桂
    9. * @since 2021/6/10 11:55
    10. */
    11. @RestController
    12. @RequestMapping("snackmachines")
    13. @RequiredArgsConstructor
    14. public class SnackMachineController {
    15. private final SnackMachineRepository snackMachineRepository;
    16. @GetMapping("/{id}")
    17. @ResponseBody
    18. public SnackMachineDto getSnackMachine(@PathVariable("id") long id) {
    19. return snackMachineRepository.findById(id).orElse(null);
    20. }
    21. @PutMapping("/{id}/{slotNumber}")
    22. public void buySnack(@PathVariable("id") long id, @PathVariable("slotNumber")
    23. int slotNumber) {
    24. SnackMachineDto snackMachineDto =
    25. snackMachineRepository.findById(id).orElse(null);
    26. SnackMachine snackMachine = snackMachineDto.convertToSnackMachine();
    27. snackMachine.buySnack(slotNumber);
    28. snackMachineRepository.save(snackMachine.convertToSnackMachineDto());
    29. }
    30. @PutMapping("/{id}/moneyInTransaction/{coinOrNote}")
    31. public void insertCoinOrNote(@PathVariable("id") long id, @PathVariable("coinOrNote") String coinOrNote) {
    32. SnackMachineDto snackMachineDto =
    33. snackMachineRepository.findById(id).orElse(null);
    34. SnackMachine snackMachine = snackMachineDto.convertToSnackMachine();
    35. if (coinOrNote.equalsIgnoreCase("Cent")) snackMachine.insertMoney(Cent);
    36. else if (coinOrNote.equalsIgnoreCase("TenCent"))
    37. snackMachine.insertMoney(TenCent);
    38. else if (coinOrNote.equalsIgnoreCase("Quarter"))
    39. snackMachine.insertMoney(Quarter);
    40. else if (coinOrNote.equalsIgnoreCase("Dollar"))
    41. snackMachine.insertMoney(Dollar);
    42. else if (coinOrNote.equalsIgnoreCase("FiveDollar"))
    43. snackMachine.insertMoney(FiveDollar);
    44. else if (coinOrNote.equalsIgnoreCase("TwentyDollar"))
    45. snackMachine.insertMoney(TwentyDollar);
    46. snackMachineRepository.save(snackMachine.convertToSnackMachineDto());
    47. }
    48. @PutMapping("/{id}/moneyInTransaction")
    49. public void returnMoney(@PathVariable("id") long id) {
    50. SnackMachineDto snackMachineDto =
    51. snackMachineRepository.findById(id).orElse(null);
    52. SnackMachine snackMachine = snackMachineDto.convertToSnackMachine();
    53. snackMachine.returnMoney();
    54. snackMachineRepository.save(snackMachine.convertToSnackMachineDto());
    55. }
    56. public Money getWholeMoney(SnackMachine snackMachine) {
    57. return Money.add(snackMachine.getMoneyInside(), snackMachine.getMoneyInTransaction());
    58. }
    59. }

    查看测试:
    image.png
    buySnack功能的缺陷先不谈,说说设计模式。