-
Notifications
You must be signed in to change notification settings - Fork 177
Feature/step1 #273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: homekeeper89
Are you sure you want to change the base?
Feature/step1 #273
Changes from all commits
a667ca0
08e4aa7
4e9315e
d6e8b70
ffc1b09
24894a1
2ba5faf
0f7a436
016250a
038d9ee
9f75afc
7487ee6
71d349f
054ae07
5fc653d
9feae87
c04204e
4cb659e
69d0724
b815b1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Product> findById(UUID productId); | ||
|
|
||
| void update(Product changedProduct); | ||
|
|
||
| List<Product> findAll(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Product> 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); | ||
|
Comment on lines
+32
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 가격 변경에 대한 로직이 현재 Product 가 아닌 ProductService 에서 수행되고 있는데요! anemic domain model 로 주로 미션해주신 것같아서 교육 목적을 위해 rich domain model 도 경험해보시면 좋을것같아요!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 즉 가격 변경에 대한 도메인 검증은 entity 에서 하고 결과를 서비스가 저장한다, 이정도로 구분하면 될까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. animic domain model 과 rich domain model 에 대해 한번 찾아보시면 좋을 것 같아요! |
||
| } | ||
|
|
||
| public List<Product> getList() { | ||
| return this.productRepository.findAll(); | ||
| } | ||
|
|
||
| public Optional<Product> findById(UUID id) { | ||
| return this.productRepository.findById(id); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<UUID> 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> 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; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 { | ||
wlroh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<UUID, Product> products = new HashMap<>(); | ||
|
|
||
| public Product save(final Product product) { | ||
| products.put(product.getId(), product); | ||
| return product; | ||
| } | ||
|
|
||
| public Optional<Product> findById(final UUID id) { | ||
| return Optional.ofNullable(products.get(id)); | ||
| } | ||
|
|
||
| public List<Product> findAll() { | ||
| return new ArrayList<>(products.values()); | ||
| } | ||
|
|
||
| public List<Product> findAllByIdIn(final List<UUID> ids) { | ||
| return products.values() | ||
| .stream() | ||
| .filter(product -> ids.contains(product.getId())) | ||
| .toList(); | ||
| } | ||
|
|
||
| public void update(final Product newProduct) { | ||
| Optional<Product> pd = this.findById(newProduct.getId()); | ||
| List<Product> pds = this.findAll(); | ||
| if(pd.isEmpty()){ | ||
| throw new NoSuchElementException("Product not found with id: " + newProduct.getId()); | ||
| } | ||
|
|
||
| this.save(newProduct); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package kitchenpos.products.tobe.domain.vo; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| public class DisplayedName { | ||
wlroh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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<String> wrongWords = new ArrayList<>(); | ||
| wrongWords.add("wrong-name"); | ||
|
|
||
| if (wrongWords.contains(word)) { | ||
| throw new IllegalArgumentException("The word '" + word + "' is not allowed."); | ||
| } | ||
|
Comment on lines
+18
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 비속어 검증하는 Profanity 대신 직접 비속어를 List 로 관리하도록 구현해주신것같은데요!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 이거 외부 서비스 대체한다 생각하고 그냥 임시코드로 구현한 것이긴 했는데 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네네 기존 레거시에 인터페이스와 구현체 모두 있어요! 그것을 활용해보시면 좋을 것 같아요! |
||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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"); | ||
| } | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Product> 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<Product> pdList = service.getList(); | ||
| assertThat(pdList).hasSize(1); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.