diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductRepositoryImpl.java b/src/main/java/kitchenpos/products/tobe/domain/ProductRepositoryImpl.java new file mode 100644 index 000000000..7278dc3ec --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductRepositoryImpl.java @@ -0,0 +1,17 @@ +package kitchenpos.products.tobe.domain; + +import kitchenpos.products.tobe.domain.entity.Product; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface ProductRepositoryImpl { + public Product save(final Product product); + + Optional findById(UUID productId); + + void update(Product changedProduct); + + List findAll(); +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/ProductService.java b/src/main/java/kitchenpos/products/tobe/domain/ProductService.java new file mode 100644 index 000000000..130afd68f --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/ProductService.java @@ -0,0 +1,45 @@ +package kitchenpos.products.tobe.domain; + +import kitchenpos.products.tobe.domain.entity.Product; +import kitchenpos.products.tobe.domain.vo.DisplayedName; +import kitchenpos.products.tobe.domain.vo.Price; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public class ProductService { + private final ProductRepositoryImpl productRepository; + + public ProductService(ProductRepositoryImpl repository) { + this.productRepository = repository; + } + + + public Product register(DisplayedName name, Price price) { + Product product = new Product(name, price); + product.register(this.productRepository); + return product; + } + + public void changeName(UUID productId, String name) throws Exception { + Optional optionalProduct = this.productRepository.findById(productId); + if (optionalProduct.isEmpty()) { + // Handle the case when the product is not found + throw new Exception("Product not found with id: " + productId); + } + + Product product = optionalProduct.get(); + Product changedProduct = new Product(productId, new DisplayedName(name), product.getPrice()); + + this.productRepository.update(changedProduct); + } + + public List getList() { + return this.productRepository.findAll(); + } + + public Optional findById(UUID id) { + return this.productRepository.findById(id); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/entity/Menu.java b/src/main/java/kitchenpos/products/tobe/domain/entity/Menu.java new file mode 100644 index 000000000..c6b41ac51 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/entity/Menu.java @@ -0,0 +1,68 @@ +package kitchenpos.products.tobe.domain.entity; + +import kitchenpos.products.tobe.domain.ProductRepositoryImpl; +import kitchenpos.products.tobe.domain.vo.DisplayedName; +import kitchenpos.products.tobe.domain.vo.Price; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public class Menu { + private final UUID id; + private final DisplayedName displayedName; + private final List productIds; + private final Price price; + private boolean isDisplay; + private final ProductRepositoryImpl repo; + + + public Menu(UUID id, DisplayedName displayedName, Price price, ProductRepositoryImpl repo) { + this.id = (id != null) ? id : UUID.randomUUID(); + this.displayedName = displayedName; + this.price = price; + this.productIds = new ArrayList<>(); + this.isDisplay = true; + this.repo = repo; + } + + public Menu(DisplayedName displayedName, Price price, ProductRepositoryImpl repo) { + this(UUID.randomUUID(), displayedName, price, repo); + } + + public UUID getId() { + return this.id; + } + + public boolean isShow() { + return this.isDisplay; + } + + public int getProductsTotalPrice() { + int price = 0; + for (UUID productId : this.productIds) { + Optional product = this.repo.findById(productId); + if (product.isPresent()) { + price += product.get().getPrice().getValue(); + } + } + return price; + } + + public DisplayedName getName() { + return this.displayedName; + } + + public void registerProduct(Product product) { + this.productIds.add(product.getId()); + this.compareProductsTotalPriceWithMenuPrice(); + } + + private void compareProductsTotalPriceWithMenuPrice() { + int totalPrice = this.getProductsTotalPrice(); + if (totalPrice > this.price.getValue()) { + this.isDisplay = false; + } + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/entity/Product.java b/src/main/java/kitchenpos/products/tobe/domain/entity/Product.java new file mode 100644 index 000000000..5a52b47d4 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/entity/Product.java @@ -0,0 +1,42 @@ +package kitchenpos.products.tobe.domain.entity; + +import kitchenpos.products.tobe.domain.ProductRepositoryImpl; +import kitchenpos.products.tobe.domain.vo.DisplayedName; +import kitchenpos.products.tobe.domain.vo.Price; + +import java.util.UUID; + + +public class Product { + private final UUID id; + private final DisplayedName displayedName; + private final Price price; + + public Product(UUID id, DisplayedName displayedName, Price price) { + this.id = (id != null) ? id : UUID.randomUUID(); + this.displayedName = displayedName; + this.price = price; + } + + public Product(DisplayedName displayedName, Price price) { + this(UUID.randomUUID(), displayedName, price); + } + + public UUID getId() { + return this.id; + } + + public Price getPrice() { + return this.price; + } + + public DisplayedName getName() { + return this.displayedName; + } + + + public void register(ProductRepositoryImpl repo) { + Product product = new Product(this.getId(), this.displayedName, this.price); + repo.save(product); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/infra/InMemoryProductRepository.java b/src/main/java/kitchenpos/products/tobe/domain/infra/InMemoryProductRepository.java new file mode 100644 index 000000000..3d195896c --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/infra/InMemoryProductRepository.java @@ -0,0 +1,40 @@ +package kitchenpos.products.tobe.domain.infra; + +import kitchenpos.products.tobe.domain.ProductRepositoryImpl; +import kitchenpos.products.tobe.domain.entity.Product; + +import java.util.*; + +public class InMemoryProductRepository implements ProductRepositoryImpl { + private final Map products = new HashMap<>(); + + public Product save(final Product product) { + products.put(product.getId(), product); + return product; + } + + public Optional findById(final UUID id) { + return Optional.ofNullable(products.get(id)); + } + + public List findAll() { + return new ArrayList<>(products.values()); + } + + public List findAllByIdIn(final List ids) { + return products.values() + .stream() + .filter(product -> ids.contains(product.getId())) + .toList(); + } + + public void update(final Product newProduct) { + Optional pd = this.findById(newProduct.getId()); + List pds = this.findAll(); + if(pd.isEmpty()){ + throw new NoSuchElementException("Product not found with id: " + newProduct.getId()); + } + + this.save(newProduct); + } +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/vo/DisplayedName.java b/src/main/java/kitchenpos/products/tobe/domain/vo/DisplayedName.java new file mode 100644 index 000000000..66a51167f --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/vo/DisplayedName.java @@ -0,0 +1,26 @@ +package kitchenpos.products.tobe.domain.vo; + +import java.util.ArrayList; +import java.util.List; + +public class DisplayedName { + private final String value; + public DisplayedName(String value){ + this.validate(value); + this.value = value; + } + + public String value(){ + return this.value; + } + + private void validate(String word) { + List wrongWords = new ArrayList<>(); + wrongWords.add("wrong-name"); + + if (wrongWords.contains(word)) { + throw new IllegalArgumentException("The word '" + word + "' is not allowed."); + } + } + +} diff --git a/src/main/java/kitchenpos/products/tobe/domain/vo/Price.java b/src/main/java/kitchenpos/products/tobe/domain/vo/Price.java new file mode 100644 index 000000000..9b75f3e97 --- /dev/null +++ b/src/main/java/kitchenpos/products/tobe/domain/vo/Price.java @@ -0,0 +1,21 @@ +package kitchenpos.products.tobe.domain.vo; + +public class Price { + final int value; + + public Price(int value) { + this.validate(value); + this.value = value; + } + + public int getValue(){ + return this.value; + } + + private void validate(int value) { + if (value <= 0) { + throw new IllegalArgumentException("Price should be over zero, not negative"); + } + } + +} diff --git a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java index b55c5ec5e..1c8e133c1 100644 --- a/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java +++ b/src/test/java/kitchenpos/products/application/InMemoryProductRepository.java @@ -13,6 +13,7 @@ public class InMemoryProductRepository implements ProductRepository { private final Map products = new HashMap<>(); + @Override public Product save(final Product product) { products.put(product.getId(), product); diff --git a/src/test/java/kitchenpos/products/application/tobe/DisplayedNameTest.java b/src/test/java/kitchenpos/products/application/tobe/DisplayedNameTest.java new file mode 100644 index 000000000..de4350ebc --- /dev/null +++ b/src/test/java/kitchenpos/products/application/tobe/DisplayedNameTest.java @@ -0,0 +1,16 @@ +package kitchenpos.products.application.tobe; + +import kitchenpos.products.tobe.domain.vo.DisplayedName; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DisplayedNameTest { + @DisplayName("상품의 이름에는 비속어가 포함될 수 없다") + @Test + void setWrongNameTest() throws Exception { + assertThatThrownBy(()->new DisplayedName("wrong-name")) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/kitchenpos/products/application/tobe/MenuTest.java b/src/test/java/kitchenpos/products/application/tobe/MenuTest.java new file mode 100644 index 000000000..0209f8b6b --- /dev/null +++ b/src/test/java/kitchenpos/products/application/tobe/MenuTest.java @@ -0,0 +1,43 @@ +package kitchenpos.products.application.tobe; + +import kitchenpos.products.tobe.domain.entity.Menu; +import kitchenpos.products.tobe.domain.entity.Product; +import kitchenpos.products.tobe.domain.infra.InMemoryProductRepository; +import kitchenpos.products.tobe.domain.vo.DisplayedName; +import kitchenpos.products.tobe.domain.vo.Price; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MenuTest { + @DisplayName("상품 가격 변동 후 메뉴 가격보다 높으면 메뉴는 숨겨진다") + @Test + void overProductTotalPriceTest() throws Exception { + InMemoryProductRepository repo = new InMemoryProductRepository(); + Menu menu = new Menu(new DisplayedName("test"), new Price(5000), repo); + assertThat(menu.isShow()).isEqualTo(true); + + int expectedPrice = 6000; + Product pd = new Product(new DisplayedName("100"), new Price(expectedPrice)); + pd.register(repo); + + menu.registerProduct(pd); + + assertThat(menu.isShow()).isEqualTo(false); + } + + @DisplayName("Menu 가격을 가져 올 수 있다") + @Test + void getProductsTotalPriceTest() throws Exception { + InMemoryProductRepository repo = new InMemoryProductRepository(); + int expectedPrice = 1000; + Product pd = new Product(new DisplayedName("100"), new Price(expectedPrice)); + pd.register(repo); + + Menu menu = new Menu(new DisplayedName("test"), new Price(5000), repo); + menu.registerProduct(pd); + + assertThat(menu.getProductsTotalPrice()).isEqualTo(expectedPrice); + } +} diff --git a/src/test/java/kitchenpos/products/application/tobe/PriceTest.java b/src/test/java/kitchenpos/products/application/tobe/PriceTest.java new file mode 100644 index 000000000..a4160c8ac --- /dev/null +++ b/src/test/java/kitchenpos/products/application/tobe/PriceTest.java @@ -0,0 +1,16 @@ +package kitchenpos.products.application.tobe; + +import kitchenpos.products.tobe.domain.vo.Price; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class PriceTest { + @DisplayName("가격은 0원 이하가 될 수 없다") + @Test + void setWrongValueTest() throws Exception { + assertThatThrownBy(()->new Price(0)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/kitchenpos/products/application/tobe/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/tobe/ProductServiceTest.java new file mode 100644 index 000000000..eff399281 --- /dev/null +++ b/src/test/java/kitchenpos/products/application/tobe/ProductServiceTest.java @@ -0,0 +1,45 @@ +package kitchenpos.products.application.tobe; + +import kitchenpos.products.tobe.domain.infra.InMemoryProductRepository; +import kitchenpos.products.tobe.domain.entity.Product; +import kitchenpos.products.tobe.domain.ProductService; +import kitchenpos.products.tobe.domain.vo.DisplayedName; +import kitchenpos.products.tobe.domain.vo.Price; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +public class ProductServiceTest { + @DisplayName("상품의 이름을 변경할 수 있다") + @Test + void changeGetNameTest() throws Exception { + Price price = new Price(10); + DisplayedName displayedName = new DisplayedName("name"); + + ProductService service = new ProductService(new InMemoryProductRepository()); + Product pd = service.register(displayedName, price); + + service.changeName(pd.getId(), "new-name"); + + Optional updatedPd = service.findById(pd.getId()); + Product upd = updatedPd.get(); + assertThat(upd.getName().value()).isEqualTo("new-name"); + } + + @DisplayName("상품을 등록할 수 있다") + @Test + void registerNewTest() { + Price price = new Price(10); + DisplayedName displayedName = new DisplayedName("name"); + + ProductService service = new ProductService(new InMemoryProductRepository()); + service.register(displayedName, price); + + List pdList = service.getList(); + assertThat(pdList).hasSize(1); + } +} diff --git a/src/test/java/kitchenpos/products/application/tobe/ProductTest.java b/src/test/java/kitchenpos/products/application/tobe/ProductTest.java new file mode 100644 index 000000000..e030af4e3 --- /dev/null +++ b/src/test/java/kitchenpos/products/application/tobe/ProductTest.java @@ -0,0 +1,40 @@ +package kitchenpos.products.application.tobe; + + +import kitchenpos.products.tobe.domain.ProductService; +import kitchenpos.products.tobe.domain.entity.Product; +import kitchenpos.products.tobe.domain.infra.InMemoryProductRepository; +import kitchenpos.products.tobe.domain.vo.DisplayedName; +import kitchenpos.products.tobe.domain.vo.Price; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +public class ProductTest { + + @DisplayName("상품을 등록할 수 있다") + @Test + void registerNewTest() { + Price price = new Price(10); + DisplayedName displayedName = new DisplayedName("name"); + + ProductService service = new ProductService(new InMemoryProductRepository()); + service.register(displayedName, price); + + List pdList = service.getList(); + assertThat(pdList).hasSize(1); + } + + @DisplayName("상품에는 가격과 이름이 필요로 하다") + @Test + void EntityPropertyTest() { + Price price = new Price(100); + DisplayedName displayedName = new DisplayedName("my-product"); + Product pd = new Product(displayedName, price); + Assertions.assertNotNull(pd); + } +}