Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions kotlin-oncall/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

## 기능 요구사항
- [x] 입력
- 비상 근무를 배정할 월과 시작 요일
`"${month},${day}"` 형식여야 함
month는 1~12, day는 월~일
- 평일 비상 근무 순번대로 사원 닉네임
`"${people1},${people2},${people3}"` 형식여야 함
중복된 이름 있으면 안됨
최대 5자
인원수: 5명~35명
- 휴일 비상 근무 순번대로 사원 닉네임
`"${people1},${people2},${people3}"` 형식여야 함
중복된 이름 있으면 안됨
평일 비상 근무 순번에 있는 사람들이 다 들어있어야함
- [x] 비상 근무 순서 배정
- 평일, 휴일 순번에 따라 인원을 배정한다.
- 순번상 특정 근무자가 연속 2일 근무하게 되는 상황에는, 다음 근무자와 순서를 바꿔 편성한다.
- [x] 출력
- `"5월 1일 월 준팍"` 형식으로 출력
- 평일이면서 법정공휴일의 경우에만 요일 뒤에 (휴일) 표기를 해야 한다.
16 changes: 16 additions & 0 deletions kotlin-oncall/docs/how-to-solve.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,20 @@

#### 1. 본인이 이해하고 구현한 내용에 기반해 '다른 근무자와 순서를 바꿔야 하는 경우'를 자신만의 예시를 들어 설명하세요. (필수)

A. 평일근무를 서고 다음 날 휴일근무를 서야 할 경우

```
- 평일 순번: 가,나,다,사,마,바,라
- 휴일 순번: 다,라,가,나,마,사,바
- 근무 예시: 가(월요일),나(화요일),다(수요일),라(목요일/휴일),사(금요일),다(토요일/휴일),...
```

B. 휴일근무를 서고 다음 날 평일근무를 서야 할 경우

```
- 평일 순번: 가,나,다,라,마,바,사
- 휴일 순번: 라,다,가,나,마,사,바
- 근무 예시: 가(월요일),나(화요일),다(수요일),라(목요일/휴일),마(금요일),다(토요일/휴일),...
```

#### 2. 요구 사항에서 제시한 앞의 날짜부터 순서를 변경하는 방법 외에 다른 방법이 있다면 어떤 방식이 있는지, 이 방법은 기존에 제시된 방식과 비교해 어떤 차이가 있는지 설명하세요. (선택)
3 changes: 2 additions & 1 deletion kotlin-oncall/src/main/kotlin/oncall/Application.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package oncall

fun main() {
TODO("프로그램 구현")
val oncallController = OncallController()
oncallController.execute()
}
18 changes: 18 additions & 0 deletions kotlin-oncall/src/main/kotlin/oncall/OncallController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package oncall

import oncall.util.InputManager
import oncall.util.OutputManager

class OncallController(
private val inputManager: InputManager = InputManager(),
private val outputManager: OutputManager = OutputManager(),
){

fun execute() {
val oncallInformationDTO = inputManager.getInput()

val workOrderService = WorkOrderService(oncallInformationDTO)
val workerList = workOrderService.getWorkerList()
outputManager.printWorkerOrder(oncallInformationDTO, workerList)
}
}
32 changes: 32 additions & 0 deletions kotlin-oncall/src/main/kotlin/oncall/WorkOrderService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package oncall

import oncall.data.Holiday
import oncall.data.OncallInformationDTO
import oncall.data.Week.Companion.isHoliday
import oncall.data.getEndDateOfMonth

class WorkOrderService(
private val oncallInformationDTO: OncallInformationDTO
) {
private val month = oncallInformationDTO.month
private val endDate = getEndDateOfMonth(month)

private val size = oncallInformationDTO.holidayWorkerList.size
private val holidayWorker = MutableList(endDate) { i -> oncallInformationDTO.holidayWorkerList[i%size] }
private val weekdayWorker = MutableList(endDate) { i -> oncallInformationDTO.weekdayWorkerList[i%size] }

fun getWorkerList(): List<String> {
val workerList = mutableListOf<String>()
for (date in 1..endDate) {
val workers = getWorkerList(date)
val workerIndex = workers.indexOfFirst { workerList.isEmpty() || it != workerList.last() }
workerList.add(workers[workerIndex])
workers.removeAt(workerIndex)
}
return workerList.toList()
}

private fun getWorkerList(date: Int) = if (isHoliday(date)) holidayWorker else weekdayWorker

private fun isHoliday(date: Int) = Holiday.isHoliday(month, date) || oncallInformationDTO.startWeek.isHoliday(date)
}
61 changes: 61 additions & 0 deletions kotlin-oncall/src/main/kotlin/oncall/data/Dates.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package oncall.data

enum class Week(
val korean: String
) {
MONDAY("월"),
TUESDAY("화"),
WEDNESDAY("수"),
THURSDAY("목"),
FRIDAY("금"),
SATURDAY("토"),
SUNDAY("일"),
;

companion object {
fun get(value: String): Week? {
return entries.firstOrNull { it.korean == value }
}
fun Week.isHoliday(date: Int): Boolean {
val holidayOrdinal = (this.ordinal + date - 1)%7
return holidayOrdinal >= SATURDAY.ordinal
}
fun Week.getWeek(month: Int, date: Int): String {
val result = entries.first { it.ordinal == (this.ordinal + date - 1)%7 }.korean
val isHoliday = if (Holiday.isHoliday(month, date)) "(휴일)" else ""
return result + isHoliday
}
}
}

enum class Holiday(
val month: Int,
val date: Int,
val korean: String

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

korean 좋네요 👍

) {
NEW_YEARS_DAY(1, 1, "신정"),
INDEPENDENCE_MOVEMENT_DAY(3, 1, "삼일절"),
CHILDREN_S_DAY(5, 5, "어린이날"),
MEMORIAL_DAY(6, 6, "현충일"),
LIBERATION_DAY(8, 15, "광복절"),
NATIONAL_FOUNDATION_DAY(10, 3, "개천절"),
HANGUL_DAY(10, 9, "한글날"),
CHRISTMAS(12, 25, "성탄절"),
;

companion object {
fun isHoliday(month: Int, date: Int): Boolean {
return entries.any { holiday ->
holiday.month == month && holiday.date == date
}
}
}
}

fun getEndDateOfMonth(month: Int): Int {
if (month !in 1..12) {
throw IllegalArgumentException()
}
val daysOfMonth = listOf(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
return daysOfMonth[month-1]
}
Comment on lines +55 to +61

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Month가 1~12 안에 속하는지 input 클래스와 여기 두 곳에서 검증하는 것 같은데, Month data 클래스를 만들어서 가지고 옮겨다니면 내부적으로 daysOfMonth도 가질 수 있어서 코드가 더 깔끔해 질 것 같아요~

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package oncall.data

data class OncallInformationDTO(
val month: Int,
val startWeek: Week,
val weekdayWorkerList: List<String>,
val holidayWorkerList: List<String>
)
34 changes: 34 additions & 0 deletions kotlin-oncall/src/main/kotlin/oncall/util/DataConverter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package oncall.util

import oncall.data.Week


class DataConverter(
private val validationChecker: ValidationChecker = ValidationChecker()
) {

fun convertDate(input: String): Pair<Int, Week> {
val split = input.split(',')
val month = split[0].toIntWithoutNull()
val week = Week.get(split[1])

validationChecker.checkDate(split.size, month, week)
return Pair(month, week!!)
}

fun convertWeekdayWorker(input: String): List<String> {
val weekdayWorker = input.split(',')
validationChecker.checkWeekdayWorker(weekdayWorker)
return weekdayWorker
}

fun convertHolidayWorker(input: String, weekdayWorker: List<String>): List<String> {
val holidayWorker = input.split(',')
validationChecker.checkHolidayWorker(weekdayWorker, holidayWorker)
return holidayWorker
}

private fun String.toIntWithoutNull(): Int {
return this.toIntOrNull() ?: throw IllegalArgumentException()
}
}
53 changes: 53 additions & 0 deletions kotlin-oncall/src/main/kotlin/oncall/util/InputManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package oncall.util

import camp.nextstep.edu.missionutils.Console
import oncall.data.OncallInformationDTO
import oncall.data.Week

class InputManager(
private val dataConverter: DataConverter = DataConverter(),
private val outputManager: OutputManager = OutputManager()
) {

fun getInput(): OncallInformationDTO {
val (month, startWeek) = getDate()
val weekdayWorker = getWeekdayWorker()
val holidayWorker = getHolidayWorker(weekdayWorker)

return OncallInformationDTO(month, startWeek, weekdayWorker, holidayWorker)
}

private fun getDate(): Pair<Int, Week> {
return getUserInput {
outputManager.printGetDateMessage()
val input = Console.readLine() ?: ""
dataConverter.convertDate(input)
}
}

private fun getWeekdayWorker(): List<String> {
return getUserInput {
outputManager.printGetWeekdayWorkerMessage()
val input = Console.readLine() ?: ""
dataConverter.convertWeekdayWorker(input)
}
}

private fun getHolidayWorker(weekdayWorker: List<String>): List<String> {
return getUserInput {
outputManager.printGetHolidayWorkerMessage()
val input = Console.readLine() ?: ""
dataConverter.convertHolidayWorker(input, weekdayWorker)
}
}

private fun <T> getUserInput(inputFunction: () -> T): T {
while (true) {
try {
return inputFunction()
} catch (_: IllegalArgumentException) {
outputManager.printExceptionMessage()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍👍

}
}
}
}
29 changes: 29 additions & 0 deletions kotlin-oncall/src/main/kotlin/oncall/util/OutputManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package oncall.util

import oncall.data.OncallInformationDTO
import oncall.data.Week.Companion.getWeek
import oncall.data.getEndDateOfMonth

class OutputManager {

fun printExceptionMessage() = println("$ERROR_PREFIX 유효하지 않은 입력 값입니다. 다시 입력해 주세요.")

fun printGetDateMessage() = print("비상 근무를 배정할 월과 시작 요일을 입력하세요> ")

fun printGetWeekdayWorkerMessage() = print("평일 비상 근무 순번대로 사원 닉네임을 입력하세요> ")

fun printGetHolidayWorkerMessage() = print("휴일 비상 근무 순번대로 사원 닉네임을 입력하세요> ")

fun printWorkerOrder(oncallInformationDTO: OncallInformationDTO, workerList: List<String>) {
val month = oncallInformationDTO.month
val endDate = getEndDateOfMonth(month)
for (date in 1..endDate) {
val week = oncallInformationDTO.startWeek.getWeek(month, date)
print("${month}월 ${date}일 $week ${workerList[date-1]}\n")
}
}

companion object {
private const val ERROR_PREFIX = "[ERROR]"
}
}
33 changes: 33 additions & 0 deletions kotlin-oncall/src/main/kotlin/oncall/util/ValidationChecker.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package oncall.util

import oncall.data.Week

class ValidationChecker {

fun checkDate(size: Int, month: Int, week: Week?) {
require(size == 2)
require(month in MONTH_RANGE)
require(week != null)
}

fun checkWeekdayWorker(worker: List<String>) {
checkWorker(worker)
}

fun checkHolidayWorker(weekdayWorker: List<String>, holidayWorker: List<String>) {
checkWorker(holidayWorker)
require(weekdayWorker.sorted() == holidayWorker.sorted())
}

private fun checkWorker(worker: List<String>) {
require(worker.distinct().size == worker.size)
require(worker.all { it.length in NICKNAME_RANGE })
require(worker.size in WORKER_RANGE)
}

companion object {
private val MONTH_RANGE = 1..12
private val NICKNAME_RANGE = 1..5
private val WORKER_RANGE = 5..35
}
}
24 changes: 24 additions & 0 deletions kotlin-oncall/src/test/kotlin/oncall/WorkOrderServiceTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package oncall

import oncall.data.OncallInformationDTO
import oncall.data.Week
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class WorkOrderServiceTest {

@Test
fun `근무자 순서 정하기` () {
val weekdayWorker = listOf("준팍","도밥","고니","수아","루루","글로","솔로스타","우코","슬링키","참새","도리")
val holidayWorker = listOf("수아","루루","글로","솔로스타","우코","슬링키","참새","도리","준팍","도밥","고니")

val oncallInformationDTO = OncallInformationDTO(5, Week.MONDAY, weekdayWorker, holidayWorker)
val result = WorkOrderService(oncallInformationDTO).getWorkerList()
val expected = listOf("준팍", "도밥", "고니", "수아", "루루", "수아", "글로", "루루", "글로")

for (index in expected.indices) {
assertEquals(expected[index], result[index])
}
}

}
45 changes: 45 additions & 0 deletions kotlin-oncall/src/test/kotlin/oncall/util/DataConverterTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package oncall.util

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows

class DataConverterTest {

private val dataConverter = DataConverter()

@Test
fun `배정 월, 시작요일 입력 성공`() {
val input = "1,월"
val (month, week) = dataConverter.convertDate(input)

assertEquals(1, month)
assertEquals("월", week)
}

@Test
fun `배정 월, 시작요일 입력 실패`() {
assertThrows<IllegalArgumentException> {
val input = "asdf,월"
val (month, week) = dataConverter.convertDate(input)
}
}

@Test
fun `평일 근무자 입력 성공`() {
val input = "a,b,c,d,e"
val worker = dataConverter.convertWeekdayWorker(input)

assertEquals(worker, listOf("a", "b", "c", "d", "e"))
}

@Test
fun `휴일 근무자 입력 성공`() {
val weekdayWorker = listOf("가","나","다","라","마","바")
val holidayWorker = "바,마,라,다,나,가"
val worker = dataConverter.convertHolidayWorker(holidayWorker, weekdayWorker)

assertEquals(worker, listOf("바", "마", "라", "다", "나", "가"))
}

}
Loading