Skip to content

Commit 24515c3

Browse files
authored
Merge pull request #70 from lee-ji-hoon/kms
[14장_김명석]
2 parents e28fda7 + 3af3a90 commit 24515c3

14 files changed

+291
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package kms.chapter14
2+
3+
abstract class BasicRatePolicy(
4+
private val feeRules: List<FeeRule>
5+
) : RatePolicy {
6+
7+
override fun calculateFee(phone: Phone): Money {
8+
return phone.getCalls()
9+
.map { call -> calculate(call) }
10+
.reduce { acc, money -> acc + money }
11+
}
12+
13+
private fun calculate(call: Call): Money {
14+
return feeRules
15+
.map { rule -> rule.calculateFee(call) }
16+
.reduce { acc, money -> acc + money }
17+
}
18+
}

src/main/kotlin/kms/chapter14/Call.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package kms.chapter14
2+
3+
import java.time.Duration
4+
import java.time.LocalDateTime
5+
6+
data class Call(
7+
val interval: DateTimeInterval,
8+
) {
9+
val duration: Duration
10+
get() = interval.duration
11+
12+
val from: LocalDateTime
13+
get() = interval.from
14+
15+
val to: LocalDateTime
16+
get() = interval.to
17+
18+
fun splitByDay() : List<DateTimeInterval> = interval.splitByDay()
19+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package kms.chapter14
2+
3+
import java.time.Duration
4+
import java.time.LocalDate
5+
import java.time.LocalDateTime
6+
import java.time.LocalTime
7+
8+
class DateTimeInterval(
9+
val from: LocalDateTime,
10+
val to: LocalDateTime,
11+
) {
12+
companion object {
13+
fun of(from: LocalDateTime, to: LocalDateTime) = DateTimeInterval(from, to)
14+
15+
fun toMidnight(from: LocalDateTime): DateTimeInterval {
16+
return DateTimeInterval(
17+
from,
18+
LocalDateTime.of(from.toLocalDate(), LocalTime.of(23, 59, 59, 999_999_999))
19+
)
20+
}
21+
22+
fun fromMidnight(to: LocalDateTime): DateTimeInterval {
23+
return DateTimeInterval(
24+
LocalDateTime.of(to.toLocalDate(), LocalTime.of(0, 0)),
25+
to
26+
)
27+
}
28+
29+
fun during(date: LocalDate): DateTimeInterval {
30+
return DateTimeInterval(
31+
LocalDateTime.of(date, LocalTime.of(0, 0)),
32+
LocalDateTime.of(date, LocalTime.of(23, 59, 59, 999_999_999))
33+
)
34+
}
35+
}
36+
37+
val duration
38+
get() = Duration.between(from, to)
39+
40+
fun splitByDay(): List<DateTimeInterval> {
41+
if (days > 0) {
42+
return splitByDay(days)
43+
}
44+
return listOf(this)
45+
}
46+
47+
private fun splitByDay(days: Long): List<DateTimeInterval> {
48+
return mutableListOf<DateTimeInterval>().apply {
49+
add(toMidnight(from))
50+
for (loop in 1 until days) {
51+
add(during(from.toLocalDate().plusDays(loop)))
52+
}
53+
add(fromMidnight(to))
54+
}
55+
56+
}
57+
58+
private val days
59+
get() = Duration.between(from.toLocalDate().atStartOfDay(), to.toLocalDate().atStartOfDay()).toDays()
60+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package kms.chapter14
2+
3+
import java.time.DayOfWeek
4+
5+
class DayOfWeekFeeCondition(
6+
private val dayOfWeeks: List<DayOfWeek>
7+
) : FeeCondition {
8+
9+
override fun findTimeIntervals(call: Call): List<DateTimeInterval> {
10+
return call.interval.splitByDay()
11+
.filter { dayOfWeeks.contains(it.from.dayOfWeek) }
12+
}
13+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package kms.chapter14
2+
3+
import java.time.Duration
4+
5+
class DurationFeeCondition(
6+
private val from: Duration,
7+
private val to: Duration,
8+
) : FeeCondition {
9+
10+
override fun findTimeIntervals(call: Call): List<DateTimeInterval> {
11+
if (call.interval.duration < from) {
12+
return emptyList()
13+
}
14+
15+
return listOf(
16+
DateTimeInterval.of(
17+
call.interval.from + from,
18+
if (call.interval.duration > to) {
19+
call.interval.from + to
20+
} else {
21+
call.interval.to
22+
}
23+
)
24+
)
25+
}
26+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package kms.chapter14
2+
3+
interface FeeCondition {
4+
5+
fun findTimeIntervals(call: Call): List<DateTimeInterval>
6+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package kms.chapter14
2+
3+
import java.time.Duration
4+
import kotlin.math.ceil
5+
6+
class FeePerDuration(
7+
private val fee: Money,
8+
private val duration: Duration,
9+
) {
10+
11+
fun calculate(interval: DateTimeInterval): Money {
12+
return fee * (ceil(interval.duration.toNanos().toDouble() / duration.toNanos()))
13+
}
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package kms.chapter14
2+
3+
class FeeRule(
4+
private val feeCondition: FeeCondition,
5+
private val feePerDuration: FeePerDuration,
6+
) {
7+
8+
fun calculateFee(call: Call): Money {
9+
return feeCondition.findTimeIntervals(call)
10+
.map { feePerDuration.calculate(it) }
11+
.reduce { acc, money -> acc + money }
12+
}
13+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package kms.chapter14
2+
3+
class FixedFeeCondition : FeeCondition {
4+
5+
override fun findTimeIntervals(call: Call): List<DateTimeInterval> {
6+
return listOf(call.interval)
7+
}
8+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package kms.chapter14
2+
3+
import java.math.BigDecimal
4+
5+
class Money(
6+
private val amount: BigDecimal,
7+
) : Comparable<Money> {
8+
operator fun plus(amount: Money): Money {
9+
return Money(this.amount.add(amount.amount))
10+
}
11+
12+
operator fun minus(amount: Money): Money {
13+
return Money(this.amount.subtract(amount.amount))
14+
}
15+
16+
operator fun times(percent: Double): Money {
17+
return Money(this.amount.multiply(BigDecimal.valueOf(percent)))
18+
}
19+
20+
operator fun times(percent: Float): Money {
21+
return Money(this.amount.multiply(BigDecimal.valueOf(percent.toDouble())))
22+
}
23+
24+
operator fun times(value: Int): Money {
25+
return Money(this.amount.multiply(BigDecimal.valueOf(value.toLong())))
26+
}
27+
28+
operator fun times(value: Long): Money {
29+
return Money(this.amount.multiply(BigDecimal.valueOf(value)))
30+
}
31+
32+
override fun compareTo(other: Money): Int {
33+
return amount.compareTo(other.amount)
34+
}
35+
36+
companion object {
37+
fun wons(amount: Long) = Money(BigDecimal.valueOf(amount))
38+
39+
val ZERO = wons(0)
40+
}
41+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package kms.chapter14
2+
3+
class Phone {
4+
5+
fun calculateFee() {
6+
7+
}
8+
9+
fun getCalls(): List<Call> {
10+
return emptyList()
11+
}
12+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
### 설계시 유의할 점
2+
3+
- 유사한 기능을 구현하기 위해 유사한 협력 패턴을 사용하기
4+
- 일관성 있는 패턴을 따르면 이해하고 확장하기 쉬워진다
5+
6+
### 캡슐화
7+
8+
- 코드 수정으로 인한 파급효과를 제어할 수 있는 모든 기법
9+
- **데이터 은닉** 뿐이 아니다
10+
- **데이터 캡슐화**
11+
- 내부에서 관리하는 데이터를 캡슐화
12+
- **메서드 캡슐화**
13+
- 클래스의 내부 행동을 캡슐화
14+
- **객체 캡슐화**
15+
- 객체의 관계를 캡슐화 한다 (합성)
16+
- **서브타입 캡슐화**
17+
- 실제 협력하는 객체를 캡슐화 한다.
18+
- 다형성의 기반
19+
20+
#### 캡슐화 방법
21+
22+
- 변하는 부분을 분리해서 타입 계층을 만든다
23+
- 변하지 않는 부분의 일부로 타입 계층을 합성한다
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package kms.chapter14
2+
3+
import kms.chapter14.Money
4+
import kms.chapter14.Phone
5+
6+
interface RatePolicy {
7+
8+
fun calculateFee(phone: Phone) : Money
9+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package kms.chapter14
2+
3+
import java.time.LocalDateTime
4+
import java.time.LocalTime
5+
6+
class TimeOfDayFeeCondition(
7+
private val from: LocalTime,
8+
private val to: LocalTime,
9+
) : FeeCondition {
10+
11+
override fun findTimeIntervals(call: Call): List<DateTimeInterval> {
12+
return call.interval.splitByDay()
13+
.filter { from(it).isBefore(to(it)) }
14+
.map {
15+
DateTimeInterval.of(
16+
LocalDateTime.of(it.from.toLocalDate(), from(it)),
17+
LocalDateTime.of(it.to.toLocalDate(), to(it)),
18+
)
19+
}
20+
}
21+
22+
private fun from(interval: DateTimeInterval): LocalTime {
23+
return if(interval.from.toLocalTime().isBefore(from)) from else interval.from.toLocalTime()
24+
}
25+
26+
private fun to(interval: DateTimeInterval): LocalTime {
27+
return if(interval.to.toLocalTime().isAfter(to)) to else interval.to.toLocalTime()
28+
}
29+
}

0 commit comments

Comments
 (0)