-
Notifications
You must be signed in to change notification settings - Fork 97
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
[3단계 - 요금 정책 추가] 우르(김현우) 미션 제출합니다 #181
Changes from 27 commits
62a03e6
752ed20
6e9cf73
f9eebfc
8eb7638
8856369
e8ec926
dac9a3b
402a425
c2f7753
6eddf3d
a42f94a
aa0e619
781c569
71c6785
4bcca05
13d1d2d
19f4307
074f896
35ea991
ff4297e
9c12f91
e90d594
028a82a
3927e12
7c02029
868b698
f385149
5f0a505
89a3d4a
7a95acc
beded7a
d320f85
c9ee774
62caf14
d3d66cb
10b95bb
966df84
c6984bb
1390aaf
727c10b
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,66 @@ | ||
package subway.domain; | ||
|
||
import java.math.BigDecimal; | ||
import java.math.RoundingMode; | ||
import java.util.Objects; | ||
|
||
public class Money { | ||
|
||
public static final Money ZERO = new Money(BigDecimal.ZERO); | ||
|
||
private final BigDecimal value; | ||
|
||
private Money(final BigDecimal value) { | ||
validateNegative(value); | ||
this.value = value; | ||
} | ||
|
||
public Money(final double value) { | ||
this(BigDecimal.valueOf(value)); | ||
} | ||
|
||
private void validateNegative(final BigDecimal value) { | ||
if (value.doubleValue() < 0) { | ||
throw new IllegalArgumentException("돈은 음수가 될 수 없습니다."); | ||
} | ||
} | ||
|
||
public Money minus(final int value) { | ||
return new Money(this.value.subtract(BigDecimal.valueOf(value))); | ||
} | ||
|
||
public Money calculateDiscountedPrice(final int percentage) { | ||
final int discountedPercentage = 100 - percentage; | ||
return new Money(this.value.multiply(BigDecimal.valueOf(discountedPercentage)) | ||
.divide(BigDecimal.valueOf(100), RoundingMode.HALF_DOWN)); | ||
} | ||
|
||
public Money add(final BigDecimal value) { | ||
return new Money(this.value.add(value)); | ||
} | ||
|
||
public Money add(final Money money) { | ||
return new Money(this.value.add(money.value)); | ||
} | ||
|
||
public Money max(final Money other) { | ||
return new Money(this.value.max(other.value)); | ||
} | ||
|
||
public double getValue() { | ||
return value.doubleValue(); | ||
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
final Money money = (Money) o; | ||
return Objects.equals(value, money.value); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(value); | ||
} | ||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package subway.domain.policy; | ||
|
||
import org.springframework.context.annotation.Primary; | ||
import org.springframework.stereotype.Component; | ||
import subway.domain.Money; | ||
import subway.domain.policy.discount.DiscountCondition; | ||
import subway.domain.policy.discount.SubwayDiscountPolicy; | ||
import subway.domain.policy.fare.SubwayFarePolicy; | ||
import subway.domain.route.Route; | ||
|
||
import java.util.List; | ||
|
||
@Primary | ||
@Component | ||
public class ChargePolicyComposite implements SubwayFarePolicy, SubwayDiscountPolicy { | ||
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. ChargePolicyComposite이 왜 SubwayFarePolicy, SubwayDiscountPolicy를 상속하는지 모르겠어요ㅠ 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. 제가 원하던 설계 방식은 그런데 요금 부과와 요금 할인은 서로 필요한 조건들이 다르기 때문에 같이 추상화시키는데 어려움이 있었습니다. 그래서 제가 내린 결론은 많이 어색한걸까요,,? |
||
|
||
private final List<SubwayFarePolicy> farePolicies; | ||
private final List<SubwayDiscountPolicy> discountPolicies; | ||
|
||
public ChargePolicyComposite( | ||
final List<SubwayFarePolicy> farePolicies, | ||
final List<SubwayDiscountPolicy> discountPolicies | ||
) { | ||
this.farePolicies = farePolicies; | ||
this.discountPolicies = discountPolicies; | ||
} | ||
|
||
@Override | ||
public Money calculate(final Route route) { | ||
return farePolicies.stream() | ||
.map(it -> it.calculate(route)) | ||
.reduce(Money.ZERO, Money::add); | ||
} | ||
choijy1705 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@Override | ||
public Money discount(final DiscountCondition discountCondition, final Money price) { | ||
return discountPolicies.stream() | ||
.reduce(price, (money, subwayDiscountPolicy) -> | ||
subwayDiscountPolicy.discount(discountCondition, money), | ||
(money1, money2) -> money2); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package subway.domain.policy.discount; | ||
|
||
import org.springframework.stereotype.Component; | ||
import subway.domain.Money; | ||
|
||
import java.util.EnumMap; | ||
import java.util.Map; | ||
|
||
@Component | ||
public class AgeDiscountPolicy implements SubwayDiscountPolicy { | ||
|
||
private static final Map<AgeGroup, DiscountValue> policyMap = new EnumMap<>(AgeGroup.class); | ||
|
||
static { | ||
policyMap.put(AgeGroup.CHILD, new DiscountValue(350, 50)); | ||
policyMap.put(AgeGroup.TEENAGER, new DiscountValue(350, 20)); | ||
policyMap.put(AgeGroup.NONE, new DiscountValue(0, 0)); | ||
} | ||
|
||
@Override | ||
public Money discount(final DiscountCondition discountCondition, final Money price) { | ||
|
||
final AgeGroup ageGroup = AgeGroup.findAgeGroup(discountCondition.getAge()); | ||
final DiscountValue discountValue = policyMap.get(ageGroup); | ||
|
||
return price.minus(discountValue.getDiscountPrice()) | ||
.calculateDiscountedPrice(discountValue.getPercent()); | ||
} | ||
|
||
private static class DiscountValue { | ||
|
||
private final int discountPrice; | ||
private final int percent; | ||
|
||
public DiscountValue(final int discountPrice, final int percent) { | ||
this.discountPrice = discountPrice; | ||
this.percent = percent; | ||
} | ||
|
||
public int getDiscountPrice() { | ||
return discountPrice; | ||
} | ||
|
||
public int getPercent() { | ||
return percent; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package subway.domain.policy.discount; | ||
|
||
import java.util.Arrays; | ||
import java.util.Objects; | ||
|
||
public enum AgeGroup { | ||
|
||
TEENAGER(13, 19), | ||
CHILD(6, 13), | ||
NONE(0, 0); | ||
|
||
private final int ageLowLimit; | ||
private final int ageHighLimit; | ||
|
||
AgeGroup(final int ageLowLimit, final int ageHighLimit) { | ||
this.ageLowLimit = ageLowLimit; | ||
this.ageHighLimit = ageHighLimit; | ||
} | ||
|
||
public static AgeGroup findAgeGroup(final Integer target) { | ||
return Arrays.stream(values()) | ||
.filter(age -> Objects.nonNull(target)) | ||
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. Integer 대신 int를 사용하면 굳이 null 체크 안해도 되겠네요! 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. 이 부분은 일부러 null을 사용했습니다 !! 요금 계산할 때 나이에 따른 요금 할인 정책이 존재하는데, 나이를 입력하지 않는 경우에는 아무 할인도 적용되지 않게 처리하기 위해서 null 을 사용했습니다 ! 만약 nullable하지 않게 하려면 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. 의도한거라면 상관없을것 같네요! |
||
.filter(age -> target >= age.ageLowLimit && target < age.ageHighLimit) | ||
.findAny() | ||
.orElse(NONE); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package subway.domain.policy.discount; | ||
|
||
public class DiscountCondition { | ||
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. 나중에 할인 조건이 생기지 않을까 하는 생각에 조건을 가지는 객체를 생성해두었습니다,, 현재는 나이 밖에 없어서 파라미터에 조금 과한 경향이 있는걸까요? 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. 개인적으로 필요한 시점에 추가하는게 가장 좋은것 같아요! |
||
|
||
private final Integer age; | ||
|
||
public DiscountCondition(final Integer age) { | ||
this.age = age; | ||
} | ||
|
||
public Integer getAge() { | ||
return age; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package subway.domain.policy.discount; | ||
|
||
import subway.domain.Money; | ||
|
||
public interface SubwayDiscountPolicy { | ||
|
||
Money discount(final DiscountCondition discountCondition, final Money price); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
https://restfulapi.net/resource-naming/
저는 해당 문서가 restful 공식문서인지 아닌지는 모르겠지만, best-practice를 보면 {id} 또는 {resource-id} 로 사용하고 있어서 이게 표준인 줄 알았습니다,,
물론 표준을 무조건 지켜야하는건 아니지만, 공식문서에서는
-
를 사용하던데, 이 부분은 같은 팀간의 컨벤션으로 결정하는 걸까요?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아하 이런것도 공식문서가 있네요
몰랐어요!
공식문서를 따르는게 좋을것 같아요