Skip to content
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

[2단계 - 주문 기능 구현] 제이미(임정수) 미션 제출합니다. #93

Merged
merged 73 commits into from
Jun 11, 2023

Conversation

JJ503
Copy link
Member

@JJ503 JJ503 commented Jun 5, 2023

안녕하세요 검프!
레벨 2의 마지막 미션의 리뷰어로 만나게 되어 정말 반갑습니다!

이번 미션은 생각보다 시간이 오래 걸려 이렇게 마지막 되어서야 리뷰를 요청드리게 되었습니다.
또한, 아직 리팩터링 할 부분, 입력 값 검증, 인수 테스트 등 추가해야 하는 부분들을 많은 상태입니다.
해당 부분들은 피드백과 함께 추가하여 다시 리뷰 요청드릴 수 있도록 하겠습니다.

1단계에서는 커밋 내역이 따로 없어 따로 범위는 지정하지 않았습니다.


이번 미션에서 재화 요소와 관련해 추가적으로 진행한 요소는 포인트입니다!
구매한 금액에 따라 포인트를 적립하고 구매 시 해당 포인트를 사용할 수 없습니다.
이때, 적립된 포인트를 이미 사용한 주문에 대해선 취소할 수 없습니다.
자세한 내용은 readme를 통해 확인하실 수 있습니다!

서버를 배포하긴 했지만, 웹 프론트가 아닌 안드로이드 팀과 협업을 진행해 따로 프론트에 대한 url은 없습니다.

이번 미션에선 제 DB 설계, entity를 사용하지 않고 domain만 사용해도 괜찮은지 등 모든 부분이 의문 투성이인 상태네요...
추가적인 질문에 대해선 코멘트로 남겨놓도록 하겠습니다.
피드백 잘 부탁드립니다! ☺️

JJ503 added 30 commits May 30, 2023 08:37
이때, 최적화란 유효기간이 짧은 순으로 사용함을 의미한다
현재로써는 사용하지 않아 제거
- PurchaseOrderInfoResponse -> PurchaseOrderItemInfoResponse
- PurchaseOrderResponse -> PurchaseOrderPageResponse

@Transactional
@Service
public class PaymentService {
Copy link
Member Author

@JJ503 JJ503 Jun 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번 미션의 경우 뼈대코드가 존재했었습니다.
한번 뼈대 코드 수정 없이 기능을 추가해보고 싶기도 했고, 뼈대 코드에 엔티티가 따로 없어 도메인으로만 작업을 진행해보고 싶었습니다.
그래서 이번 미션에서는 엔티티 없이 모두 도메인으로만 구현을 수행했습니다.
(사실 테이블 필드와 1:1 매핑은 아니기에 엔티티라고 보기 어려울 수도 있습니다...!)
이때, 든 의문점이 몇 가지 있습니다.

1. 특정 도메인만 엔티티를 가져도 괜찮은가?

특정 도메인은 도메인과 엔티티를 갖게 바라보고 특정 도메인은 엔티티와 분리해도 괜찮은지 궁금합니다.
이때, 제가 생각한 장단점은 다음과 같습니다.
필요한 도메인만 엔티티를 만든다면 불필요한 클래스를 줄일 수 있다는 장점이 있습니다.
하지만, 도메인과 엔티티의 통일성이 떨어진다는 문제점이 있습니다.
그래서 이번 미션에서는 통일성을 위해 모든 도메인을 엔티티로써도 바라보고 구현을 진행했습니다.

2. 엔티티를 따로 두지 않는 경우 해당 도메인이 테이블과 1:1 매핑되지 않아도 괜찮은가?

현재 제 코드의 경우 엔티티는 따로 없지만, 도메인을 엔티티 겸임으로 사용하고 있습니다.
정확히는 join을 위해 vo와 같은 객체를 만드는 것처럼 도메인을 사용하는 것으로 보입니다.
현재 구현만을 위해선 괜찮다고 생각하지만, 구현을 완료한 후에 확장성 측면에서는 좋지 못하다는 느낌을 받긴 했습니다.
이로인해 dao의 sql 쿼리문 중 select문의 경우 모든 필드를 불러오는 것이 아니라 도메인에서 필요한 필드만 불러오기 때문입니다.
이러한 점이 도메인과 테이블이 서로 너무 의존을 하고 있다는 문제점이 있다고 생각했습니다.

그렇다면 확장성을 위해 거의 대부분의 경우 entity와 repository가 있는 것이 좋다는 생각이 들게 되었습니다.
하지만, entity와 domain이 정말 1:1 매핑이 되었을 때도 분리가 필요할지,
respository를 사용한 경우 추상화가 한 단계 더 발생해 코드 이해에 어려움이 생길 텐데 항상 만들어두는 것이 좋은지
혹은 정말 필요할 때 만드는 것이 좋은지 궁금합니다.
또한, 그때에 대한 검프의 기준이 궁금합니다!


이번 미션에서는 기존 코드에 기능을 추가하고 엔티티를 따로 만들지 않았을 때의 장단점을 경험해보고 싶었습니다.
그런데 아마 위와 같은 이유로 리팩토링을 수행할 때 entity와 repository를 만들 것 같다는 생각을 하게 되었습니다.
그런데 검프의 의견이 궁금해 여쭤봅니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분은 도메인 vs 엔티티로 보면 안될것 같아요.

엔티티는 도메인을 표현하기위한 전술적 설계인거지 도메인과 대비되는 개념은 아니에요.

오히려 값객체 vs 엔티티로 표현되는게 옳다고 생각되니다.

해당 정의에서 엔티티란.

  • 고유식별자를 가지고 변화 가능성을 가진 객체

값 객체란

  • 어떠한 개념을 값으로 나타낼 때 사용되는 불변한 특성을 가진 객체

제이미가 말씀주신 도메인은 사실 상 엔티티고, 엔티티라 말씀해주신 부분은 DB와 객체를 매핑하기 위한 객체라고 볼 수 있는데,
결국 둘다 엔티티라고 볼 수 있어요.

하이버네이트에서 제공하는 @entity라는 애너테이션만 엔티티가 아님을 명시해야합니다.

결국, 조금더 나아가서, 매핑 역할을 할 엔티티가 필요한가? 로 질문을 단순화 해볼 수 있을 것 같은데요.

이경우 저는 최대한 값 객체로 도메인을 표현해 보려고 하지만 엔티티의 특성이 필요해지는 순간(고유식별자로 동등성 비교 필요, 변화 필요)에는 만드는 편입니다 :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇군요..!

db 테이블과 완전히 1:1 매핑이 되지 않는다면 값 객체라고 생각했는데 고유의 식별자를 갖고 있기에 엔티티라고 보는군요!


이경우 저는 최대한 값 객체로 도메인을 표현해 보려고 하지만 엔티티의 특성이 필요해지는 순간(고유식별자로 동등성 비교 필요, 변화 필요)에는 만드는 편입니다 :)

이 문장에 대해 제가 잘 이해하지 못한 것 같습니다.
해당 의견의 전제는 엔티티가 따로 없는 경우일까요?
그리고 만약 엔티티의 특성이 필요해진다면 엔티티를 따로 만드는 것이 아닌 id와 같은 식별자를 도메인에 추가한다는 의미일까요?
맞다면 이 경우 도메인과 테이블의 연관 관계가 높아질 수도 있다는 생각을 하게 되었습니다.
그렇게 된다면 무언가 하나가 바뀌게 되면 연달아 다른 것도 바뀌어야 하게 되는 문제가 발생할 수가 있다고 생각합니다.

즉, 확장성 측면에서는 문제가 될 수도 있지 않을까라는 생각을 하게 되었습니다.
그럼에도 따로 분리하지 않고 값 객체 도메인으로 먼저 표현하려고 하시는 이유가 있으실까요?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

대다수의 요구사항은 값객체로도 충분히 표현히 가능하기 때문입니다.

관점을 테이블에서 도메인으로 조금만 옮겨볼게요.

엔티티(도메인)가 테이블의 매핑을 주도하는 것이고, 테이블의 변경시점은 결국 도메인의 요구사항 추가/수정이 필요한 시점입니다.

즉, 연관관계 기준으로 봤을 때, 테이블이 도메인을 의존하고 있다라고 생각하시고 개발한다면 좋을 것 같아요.

실제로 의존관계가 그렇진 않지만, 테이블의 변경이유를 위의 주장과 같이 도메인의 추가/수정이 이뤄나기 때문이라 생각한다면, 확장에 유리한 개발이 가능할거라 생각해요.

Comment on lines +39 to +48
public PaymentService(ProductDao productDao, PurchaseOrderDao purchaseOrderDao,
PurchaseOrderItemDao purchaseOrderItemDao, MemberRewardPointDao memberRewardPointDao,
OrderMemberUsedPointDao orderMemberUsedPointDao, CartItemDao cartItemDao) {
this.productDao = productDao;
this.purchaseOrderDao = purchaseOrderDao;
this.purchaseOrderItemDao = purchaseOrderItemDao;
this.memberRewardPointDao = memberRewardPointDao;
this.orderMemberUsedPointDao = orderMemberUsedPointDao;
this.cartItemDao = cartItemDao;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PurchaseOrderServicePaymentService는 모두 주문과 관련된 기능을 수행합니다.
그런데 주문 기능 중 조회의 경우 위와 같이 많은 테이블을 참조할 필요가 없습니다.
그래서 주문 조회와 구매 및 취소에 대한 서비스를 분리했는데 어떤지 궁금합니다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 책임이 1개 이상이면 분리하는것을 선호하는 편이여서요 :)
다만 처음 개발을 진행할때는 너무 먼곳을 생각하지는 않아도 될 것 같아요.

제이미 말씀처럼 CUD와 R을 분리하고 싶다면 분리해도 어색하지 않아보여요.

import java.time.LocalDateTime;
import java.util.Objects;

public class Point {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

도메인과 엔티티를 동일하게 봤을 때의 동들성 재정의 형식이 궁금합니다.
기존의 제 생각은 엔티티의 경우 id만 동등성 요소에 포함하고 도메인의 경우 모든 요소에 대한 동등성을 재정의해주어야 한다고 생각했습니다.
그런데 이 둘을 동일하게 바라보았을 때의 동등성 재정의는 어떻게 해줘야 할지 고민이 되네요...!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당부분은 위의 코멘트와 동일합니다. #93 (comment)

Comment on lines +20 to +23
public static ProductResponse of(Product product) {
return new ProductResponse(product.getId(), product.getName(), product.getPrice(), product.getImageUrl());
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 ProductResponse의 경우 dto에서 정적 팩토리 메서드를 사용해 도메인에서 dto로 변환해주고 있습니다.
그런데 이와 같이 수행한다면 dto가 도메인을 알고 있어 의존관계가 되는 것이 아닐까 하는 걱정이 있습니다.
그래서 변환의 과정을 어디서 수행하는 것이 가장 이상적 일지 궁금합니다.

제가 생각한 방법은 다음 두 가지입니다.

  1. service에서 수행
  2. 변환을 위한 mapper 객체 생성

현재 2번 방법이 이상적이지 않을까 싶은데 다른 더 좋은 방법이 있는지 궁금합니다!
또한, 2번 방법이 괜찮다면 dto와 domain의 관계가 아닌 domain과 entity의 관계의 변환도 해당 mapper에 있어도 괜찮은지 궁금합니다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mapper 만들어 mapping layer를 만드는게 가장 이상적일거라 생각이 들어요.

다만 자칫하다 과해질 수 있기 때문에, dto에서 변환을위해 도메인을 의존하는 것은 어느정도 괜찮아 보입니다 :)

의존관계 상 dto의 변경이 도메인에 영향을 끼치진 않으니까요.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 dto에서 변환을 하게 된다면 dto가 도메인을 알고 있는 것이 문제가 되지 않을까? 라는 생각을 했었는데 그렇지만은 않군요!
도메인의 변경이 dto에 영향을 주기 때문이라 생각했는데 영향을 끼치는 것은 아니기에 괜찮겠네요!

Copy link

@Livenow14 Livenow14 Jun 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

도메인의 변경이 dto에 영향을 주는것은 어쩔수 없는 요구사항의 추가/수정이 있었기 떄문이기에, 컴파일예외 등으로 미리 잡을 수 있을 것 같아요

Comment on lines +151 to +153
PurchaseOrderResponse response = requestShowPurchaseOrder(1L);

assertThat(response).isEqualTo(expectResponse);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

통합 테스트 및 인수 테스트 수행 시 db에 저장, 삭제, 업데이트와 같은 이벤트가 잘 수행되었는지 확인하는 것이 좋을까요?
혹은 이전에 수행한 단위 테스트를 믿는 것이 좋을까요?

현재는 단위 테스트를 믿는다는 가정하에 반환되는 객체만 확인하는 것으로 진행했습니다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 통합테스트의 범주로 생각했을때, DB의 경우엔 실제 DB를 의존하는 편입니다. (프로젝트 내부에서 관리되기 때문, 관리 의존성)

단위테스트에서는 db를 의존하지 않지만, 하나의 단위를 테스트 하기 위해 다른 의존성이 필요하다면 실제 의존성을 주입하곤 해요.

저에게는 테스트에 DB가 포함되어 있냐 없냐가 단위테스트/통합테스트를 구분하는 기준입니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇군요!
저는 dao 테스트를 단위 테스트라고 보고 있었고 잘 수행되었는지에 대한 결과를 DB에 의존해 확인했습니다.
그렇다면 검프는 dao 테스트는 따로 진행하지 않는 것일까요?
혹은 dao 테스트는 통합 테스트로 바라보신 걸까요?

또한, 만약 dao + DB에 대한 테스트가 진행된 경우에도 컨트롤러 ↔ dao까지의 통합테스트에서 컨트롤러 반환 값 외에도 DB에 제대로 데이터가 들어갔는지 등에 대한 확인이 필요하다고 생각하시는 걸까요?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dao의 테스트의 경우 복잡한 쿼리 로직 이외에는 크게 작성하지 않는 편입니다.
물론 돌다리도 두드려 보듯이 테스트 해보는것이 좋긴하지만, 저장/삭제/수정 등의 로직은 중복되는 경우가 많아서요.

@@ -0,0 +1,39 @@
INSERT INTO product (name, price, image_url) VALUES ('치킨', 10000, 'testImage1');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트에 사용할 데이터를 미리 넣어두고 사용하는 것에 대해 어떻게 생각하시나요?
우선 시간이 부족하기도 하고 계속 반복적으로 데이터 추가를 하기 귀찮아 데이터를 미리 넣어두고 수행했습니다.
이때, 발생할 수 있는 문제점으로는 후반에 테스트 작업을 위해 추가한 데이터가 앞서 수행한 테스트 작업들에 영향을 줄 수 있다는 점이 있습니다.
현재는 위의 문제점만 느낀 상태인데 이렇게 데이터를 미리 넣어두고 사용하는 것에 대해 어떻게 생각하시는지 궁금합니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이경우엔 애플리케이션 내부에서 동작을 유추해볼 수 없기 때문에 프로젝트 초기단계에서만 사용하고,
향후 테스트 해야하는 부분에서는 Fixture를 관리해서 사용하는게 좋다고 생각해요.

Copy link

@Livenow14 Livenow14 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 제이미 구현잘해주셨어요 :)

간단한 피드백 작성했는데 반영해주시면 좋을 것 같아요!!

단위테스트가 조금 부족한 것 같아 조금더 추가해주시면 좋을 것 같아요

화이팅입니다

```


- [x] 단일 주문 조회

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asciidoc 을 활용해봐도 좋을 것 같아요 :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asciidoc은 처음 들어봤네요!
찾아보니 RestDocs를 만들고 사용하는 것 같은데 맞을까요?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 맞습니다 :)


import java.util.List;
import java.util.stream.Collectors;

@Transactional

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CUD와 R 서비스를 분리해보는건 어떨까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀해 주신 대로 CartItemService와 ReadCartItem으로 분리해 보았습니다.
그런데 CartItemService의 경우 로직이 복잡하다거나 클래스 내 코드의 양이 많은 편은 아닌 상태입니다.
그럼에도 메서드에 @Transactional(readOnly = true)을 부여해 주는 것보단, R 서비스를 분리하고 시작하는 것이 좋다는 의미가 맞을까요?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

학습차 나눠보는 경험을 해보면 좋을 것 같아서 제안드린 커멘트였어요..!
(기존 코드도 충분히 좋은 코드입니다)

처음엔 하나의 서비스에서 모든 로직을 담게 작성하고, 향후 책임이 너무 커진다 싶으면 분리해주시면 될 것 같아요 :)

Comment on lines 53 to 55
int payment = getPayment(orderItems);
payment -= purchaseOrderRequest.getUsedPoint();
int point = SavePointPolicy.calculate(payment);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메소드를 분리해봐도 좋겟네요 :)

Comment on lines 52 to 71
List<PurchaseOrderItem> orderItems = getPurchaseOrderItems(purchaseOrderRequest);
int payment = getPayment(orderItems);
payment -= purchaseOrderRequest.getUsedPoint();
int point = SavePointPolicy.calculate(payment);

LocalDateTime now = LocalDateTime.now();
Point rewardPoint = new Point(point, now, SavePointPolicy.getExpiredAt(now));
PurchaseOrderInfo purchaseOrderInfo = new PurchaseOrderInfo(member, now, payment, purchaseOrderRequest.getUsedPoint());

MemberPoints memberPoints = new MemberPoints(member, memberRewardPointDao.getAllByMemberId(member.getId()));
List<Point> pointsSnapshot = getPointSnapshot(memberPoints.getPoints());

List<UsedPoint> usedPoints = memberPoints.usedPoint(purchaseOrderRequest.getUsedPoint());

PurchaseOrder purchaseOrder = new PurchaseOrder(purchaseOrderInfo, orderItems, usedPoints, rewardPoint);
Long orderId = savePurchaseOrder(member, purchaseOrder);
deleteCartItems(member, purchaseOrder.getPurchaseOrderItems());
updateChangePoints(memberPoints.getPoints(), pointsSnapshot);
return orderId;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각 단계를 메서드로 분리해서 추상화 해보면 좋겠네요.

한 메서드에 15줄 이상 작성하지 않는다... 를 지켜주시면 좋겠어요

Comment on lines +167 to +172
private void checkCanceledOrder(Long orderId) {
if (purchaseOrderDao.isCanceled(orderId)) {
throw new IllegalArgumentException("이미 취소된 주문입니다.");
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check 하는 부분들은 policy로 관리해봐도 좋을 것 같아요.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check에 대해 policy로 관리하지 않은 이유는 다음과 같습니다.
현재 취소되었는지에 대해 확인하는 것은 PurchaseOrderDao에서 수행합니다.
그렇게 된다면 굳이 객체를 만들지 않고 EXIST를 통해 간단하게 확인이 가능하기 때문입니다.
그런데 policy로 빼게 된다면 객체를 생성해 관리해줘야 한다는 단점이 있을 것이라 생각했습니다.
혹시 이에 대해 어떻게 생각하시는지 궁금합니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분은 제이미의 의도가 명확하다면 현재로도 좋아 보입니다 :)

policy를 만든다하더라도 모든 순간에 객체를 만들진 않아서요 :)

Comment on lines 76 to 85
if (purchaseOrderInfoById.isPresent()) {
PurchaseOrderInfo purchaseOrderInfo = purchaseOrderInfoById.get();
List<PurchaseOrderItemResponse> purchaseOrderItemResponses = getPurchaseOrderItemResponses(orderId);
Point savedPoint = memberRewardPointDao.getPointByOrderId(orderId).orElse(new Point(0, null, null));
return new PurchaseOrderResponse(orderId, purchaseOrderInfo.getOrderAt(),
purchaseOrderInfo.getStatus(), purchaseOrderInfo.getPayment(),
purchaseOrderInfo.getUsedPoint(), savedPoint.calculatePointByExpired(), purchaseOrderItemResponses);
}
throw new IllegalArgumentException("해당 상품이 존재하지 않습니다.");
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

early return을 활용해봐도 좋을 것 같아요

        if (purchaseOrderInfoById.isEmpty()){
    throw new IllegalArgumentException("해당 상품이 존재하지 않습니다.");

    } 

NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.insertAction = new SimpleJdbcInsert(dataSource)
.withTableName("order_member_used_point")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

table_name이 명시되어있으니 좋네요 :)

Comment on lines 26 to 31
private void canUsedPoint(int usedPoint) {
if (getUsablePoints() < usedPoint) {
throw new PointException("사용하려는 포인트가 부족합니다.");
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PointPolicy로 관리될 수 있겟네요

Copy link
Member Author

@JJ503 JJ503 Jun 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PointPolicy 클래스로 분리해 보았습니다.
그런데 검프가 생각하신 방향이 맞는지 잘 모르겠네요..!
확인해 주시면 감사하겠습니다! (로직 바로 가기)

Comment on lines +7 to +10
RANGE_1(0, 50_000, 0.05),
RANGE_2(50_000, 100_000, 0.08),
RANGE_3(100_000, Integer.MAX_VALUE, 0.1);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

매직 넘버랑 다른게 있을까요??

enum이 아닌, 추상객체를 활용해보는건 어덜까요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

매직 넘버와의 차이는 금액을 입력하면 자동으로 금액에 따른 range를 반환한다는 점이 있습니다.

그런데 추상객체가 혹시 뭔지 알 수 있을까요?
검색을 해봐도 enum을 추상객체로 바꾸라는 의미가 정확히 이해가 되지 않아 여쭤봅니다..!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum -> abstract class 였는데, 제가 코멘트를 잘못드렸네요..!

enum -> abstract class, 혹은 abstract class -> enum 으로 리펙터링 하는 경우가 많아서, 한번 학습차 말씀드린 내용이었어요 :)

import java.util.Comparator;
import java.util.List;

public class PointPolicy {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PointPolicy

리뷰 답변 참고

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alidation을 관리하는 객체로 뺐다는 점에서 제 의도와 동일하네요 :)

다만, 해당 policy 객체를 조금더 효율적으로 사용하는 방법은 무엇이 있을까를 3단계 전에 많이 고민해보시면 좋을 것 같아요!

@JJ503
Copy link
Member Author

JJ503 commented Jun 9, 2023

안녕하세요 검프!
결국 완성하지 못한 제이미입니다... 🥲
오늘 6시까지 머지가 되어야 한다고 해서 급하게 제출하게 되었습니다.

코멘트로 이해가 되지 않은 피드백에 대한 질문을 작성해 보았습니다.
해당 답변과 함께 테스트 및 RestDocs를 추가적으로 작성해 볼 수 있도록 하겠습니다..!

마지막까지 감사합니다 ☺️

Copy link

@Livenow14 Livenow14 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 제에미..! 구현 및 리팩터링 잘해주셨어요 :)

처음 조금 어려운 피드백들을 많이 드렸는데 잘 구현해주신 것 같아요..!

질문에 대한 답변들은 각 코멘트들에 추가로 남겼습니다!

다음 미션도 화이팅이에요 수고많으셨습니다.

import java.util.Comparator;
import java.util.List;

public class PointPolicy {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alidation을 관리하는 객체로 뺐다는 점에서 제 의도와 동일하네요 :)

다만, 해당 policy 객체를 조금더 효율적으로 사용하는 방법은 무엇이 있을까를 3단계 전에 많이 고민해보시면 좋을 것 같아요!

Comment on lines +55 to +61
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PurchaseOrderItemInfoResponse that = (PurchaseOrderItemInfoResponse) o;
return payAmount == that.payAmount && totalProductCount == that.totalProductCount && Objects.equals(orderId, that.orderId) && Objects.equals(orderAt, that.orderAt) && Objects.equals(productName, that.productName) && Objects.equals(productImageUrl, that.productImageUrl);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

response를 동등성 비교까지 해야할 이유가 있을가요..?

@Livenow14 Livenow14 merged commit 15d303a into woowacourse:jj503 Jun 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants