Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
73592de
docs: 기능요구사항 작성
LeeYongIn0517 Nov 26, 2024
db3341d
feat: 날짜, 휴일, 요일 상수화 및 객체 model 생성
LeeYongIn0517 Nov 26, 2024
52048d4
docs: 기능 요구사항 model 정리
LeeYongIn0517 Nov 26, 2024
d9e6417
feat: CalendarGenerator 객체 및 캘린더 생성 구현
LeeYongIn0517 Nov 26, 2024
7e7ff2a
feat: CalendarGenerator 생성결과 반환값 확인
LeeYongIn0517 Nov 26, 2024
0db4688
feat: 입력 요구 사항 구현
LeeYongIn0517 Nov 26, 2024
b184035
feat: OnCallGenerator 생성
LeeYongIn0517 Nov 27, 2024
b0b4a8c
Delete: 초기화
LeeYongIn0517 Nov 27, 2024
7aa1a68
feat: model 생성
LeeYongIn0517 Nov 27, 2024
b4fa760
feat: 스케쥴 객체 초기화 기능 구현
LeeYongIn0517 Nov 27, 2024
affc64d
feat(Validator): 월, 시작요일 예외처리
DaeYoungee Nov 27, 2024
897075a
feat(Validator): 근무자 이름 예외처리ing...
DaeYoungee Nov 28, 2024
7535ebc
docs: 알고리즘 아이디어 설명 추가
LeeYongIn0517 Nov 29, 2024
8a6d29a
fix: InputView 및 유효성 검사 로직 오류 수정
LeeYongIn0517 Nov 29, 2024
b196ed7
feat: 결과 출력 기능 구현
LeeYongIn0517 Nov 29, 2024
fa7fdfa
feat: OnCallWorkerGenerator를 통한 근무자 배정 로직 추가
LeeYongIn0517 Nov 29, 2024
4303ddf
refactor(OnCallWorkerGenerator): 메서드 추상화
DaeYoungee Nov 29, 2024
444974d
refactor: Worker 데이터 클래스 생성 및 적용
LeeYongIn0517 Nov 29, 2024
06685ec
delete: 필요없는 클래스 삭제
LeeYongIn0517 Nov 29, 2024
dd7446d
refactor: Validator 메서드 분리
LeeYongIn0517 Nov 29, 2024
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
8 changes: 8 additions & 0 deletions local.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Wed Nov 27 15:07:01 KST 2024
sdk.dir=/Users/jeongdaeyeong/Library/Android/sdk
15 changes: 14 additions & 1 deletion src/main/kotlin/oncall/Application.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
package oncall

import oncall.controller.OnCallController
import oncall.domain.OnCallMonthGenerator
import oncall.domain.OnCallWorkerGenerator
import oncall.util.Validator
import oncall.view.InputView
import oncall.view.OutputView

fun main() {
TODO("프로그램 구현")
val inputView = InputView()
val valildator = Validator()
val onCallMonthGenerator = OnCallMonthGenerator()
val onCallWorkerGenerator = OnCallWorkerGenerator()
val outputView = OutputView()
val controller = OnCallController(inputView, valildator, onCallMonthGenerator, onCallWorkerGenerator, outputView)
controller.run()
}
56 changes: 56 additions & 0 deletions src/main/kotlin/oncall/controller/OnCallController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package oncall.controller

import oncall.domain.OnCallMonthGenerator
import oncall.domain.OnCallWorkerGenerator
import oncall.model.OnCallWorkSchedule
import oncall.model.Week
import oncall.model.Worker
import oncall.util.Validator
import oncall.util.retryInput
import oncall.view.InputView
import oncall.view.OutputView

class OnCallController(
private val inputView: InputView,
private val validator: Validator,
private val onCallMonthGenerator: OnCallMonthGenerator,
private val onCallWorkerGenerator: OnCallWorkerGenerator,
private val outputView: OutputView
) {

private val onCallWorkSchedule = OnCallWorkSchedule()

fun run() {
val (month, week) = inputMonthAndStartDayOfWeek()
val initializedOnCallMonth = onCallMonthGenerator.generateOnCallMonth(month, week)
val (weekForce, weekendForce) = inputWeekdaysWorkforce()
val result = onCallWorkerGenerator.generateOnCallWorker(
initializedOnCallMonth.toMutableList(),
weekForce.map { Worker(name = it) }.toMutableList(),
weekendForce.map { Worker(name = it) }.toMutableList()
)
onCallWorkSchedule.updateResult(result)
outputView.printOnCallWorkSchedule(onCallWorkSchedule)
}

private fun inputMonthAndStartDayOfWeek(): Pair<Int, Week> {
return retryInput {
val monthAndStartDayOfWeek = inputView.inputMonthAndStartDayOfWeek()
return@retryInput validator.validateInputMonthAndStartDayOfWeek(monthAndStartDayOfWeek)
}
}

private fun inputWeekdaysWorkforce(): Pair<List<String>, List<String>> {
return retryInput {
val weekWorkforce = inputView.inputWeekdaysWorkforce()
val weekendWorkforce = inputView.inputWeekendsWorkforce()
val validatedWeekWorkForce = validator.validateInputWorkforce(weekWorkforce)
val validatedWeekendWorkForce = validator.validateInputWorkforce(weekendWorkforce)
validator.validateWeekAndWeekendScehdule(
validatedWeekWorkForce.toSet(),
validatedWeekendWorkForce.toSet()
)
return@retryInput Pair(validatedWeekWorkForce, validatedWeekendWorkForce)
}
}
}
36 changes: 36 additions & 0 deletions src/main/kotlin/oncall/domain/OnCallMonthGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package oncall.domain

import oncall.model.DailyOnCall
import oncall.model.Holiday
import oncall.model.MonthlyStartAndEndDay
import oncall.model.Week

class OnCallMonthGenerator {
fun generateOnCallMonth(month: Int, startDay: Week): List<DailyOnCall> {
val result = mutableListOf<DailyOnCall>()
val week = Week.entries
var index = Week.entries.indexOf(startDay)
val targetMonth = MonthlyStartAndEndDay.entries.get(month - 1)
for (date in targetMonth.start..targetMonth.end) {
val isHoliday = isHoliday(month, date)
val day = week.get(index)
result.add(DailyOnCall(month, date, day, null, isHoliday(month, date), isWeekendAndHoliday(isHoliday, day)))
index = (index + 1) % week.size
}
return result
}

fun isHoliday(month: Int, date: Int): Boolean {
Holiday.entries.forEach {
if (it.month == month && it.date == date) {
return true
}
}
return false
}

fun isWeekendAndHoliday(isHoliday: Boolean, day: Week): Boolean {
return isHoliday == true || day in listOf(Week.SAT, Week.SUN)
}
}

95 changes: 95 additions & 0 deletions src/main/kotlin/oncall/domain/OnCallWorkerGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package oncall.domain

import oncall.model.DailyOnCall
import oncall.model.Worker

class OnCallWorkerGenerator {

fun generateOnCallWorker(
workSchedule: MutableList<DailyOnCall>,
weekForce: MutableList<Worker>,
weekendForce: MutableList<Worker>,
): List<DailyOnCall> {
var previousWorker: Worker? = null
return workSchedule.map {
val currentWorker = getCurrentWorker(
previousWorker,
it.isWeekendAndHoliday,
weekendForce,
weekForce
)

val workers =
getWeekendsOrWeekdaysWorkers(it.isWeekendAndHoliday, weekendForce, weekForce)

workers.add(currentWorker)
previousWorker = currentWorker
it.copy(worker = currentWorker)
}
}

private fun getCurrentWorker(
previousWorker: Worker?,
isWeekendAndHoliday: Boolean,
weekendForce: MutableList<Worker>,
weekForce: MutableList<Worker>,
): Worker {
val willCurrentWorker = getWillCurrentWorker(isWeekendAndHoliday, weekendForce, weekForce)
return generateBackupWorker(
previousWorker == willCurrentWorker,
willCurrentWorker,
isWeekendAndHoliday,
weekendForce,
weekForce
)
}

private fun getWillCurrentWorker(
isWeekendAndHoliday: Boolean,
weekendForce: MutableList<Worker>,
weekForce: MutableList<Worker>,
): Worker {
if (isWeekendAndHoliday) {
return weekendForce.removeFirst()
}
return weekForce.removeFirst()
}

private fun generateBackupWorker(
isIdenticalWorker: Boolean,
currentWorker: Worker,
isWeekendAndHoliday: Boolean,
weekendForce: MutableList<Worker>,
weekForce: MutableList<Worker>,
): Worker {
if (isIdenticalWorker) {
return popNextWorker(currentWorker, isWeekendAndHoliday, weekendForce, weekForce)
}
return currentWorker
}

private fun popNextWorker(
currentWorker: Worker,
isWeekendAndHoliday: Boolean,
weekendForce: MutableList<Worker>,
weekForce: MutableList<Worker>,
): Worker {
val workers = getWeekendsOrWeekdaysWorkers(isWeekendAndHoliday, weekendForce, weekForce)
val currentAssignWorker = workers.removeFirst()
workers.add(FIRST_INDEX, currentWorker)
return currentAssignWorker
}

private fun getWeekendsOrWeekdaysWorkers(
isWeekendAndHoliday: Boolean,
weekendForce: MutableList<Worker>,
weekForce: MutableList<Worker>,
): MutableList<Worker> {
if (isWeekendAndHoliday) return weekendForce
return weekForce
}

companion object {
const val FIRST_INDEX = 0
}
}
10 changes: 10 additions & 0 deletions src/main/kotlin/oncall/model/DailyOnCall.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package oncall.model

data class DailyOnCall(
val month: Int,
val date: Int,
val dayOfWeek: Week,
val worker: Worker?,
val isHoliday: Boolean, //(휴일) 표시를 넣기 위한 지표
val isWeekendAndHoliday: Boolean //법정공휴일 근무자
)
5 changes: 5 additions & 0 deletions src/main/kotlin/oncall/model/Holiday.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package oncall.model

enum class Holiday(val month: Int, val date: Int) {
신정(1, 1), 삼일절(3, 1), 어린이날(5, 5), 현충일(6, 6), 광복절(8, 15), 개천절(10, 3), 한글날(10, 9), 성탄절(12, 25)
}
9 changes: 9 additions & 0 deletions src/main/kotlin/oncall/model/MonthlyStartAndEndDay.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package oncall.model

enum class MonthlyStartAndEndDay(val start: Int, val end: Int) {
JAN(1, 31), FEB(1, 28), MAR(1, 31), APR(1, 30), MAY(1, 31), JUN(1, 30), JUL(1, 31), AUG(1, 31), SEP(1, 30), OCT(
1,
31
),
NOB(1, 30), DEC(1, 31)
}
13 changes: 13 additions & 0 deletions src/main/kotlin/oncall/model/OnCallWorkSchedule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package oncall.model

class OnCallWorkSchedule {
private val onCallWorkSchedule: MutableList<DailyOnCall> = mutableListOf()

fun updateResult(initializedSchedule: List<DailyOnCall>) {
onCallWorkSchedule.addAll(initializedSchedule)
}

fun getResult(): List<DailyOnCall> {
return onCallWorkSchedule
}
}
5 changes: 5 additions & 0 deletions src/main/kotlin/oncall/model/Week.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package oncall.model

enum class Week(val dayName: String) {
SUN("일"), MON("월"), TUE("화"), WED("수"), THU("목"), FRI("금"), SAT("토")
}
3 changes: 3 additions & 0 deletions src/main/kotlin/oncall/model/Worker.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package oncall.model

data class Worker(val name: String)
70 changes: 70 additions & 0 deletions src/main/kotlin/oncall/util/Validator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package oncall.util

import oncall.model.Week

class Validator {

fun validateInputMonthAndStartDayOfWeek(input: String): Pair<Int, Week> {
val i = input.replace(" ", "").split(",")
val month = validateInputMonth(i[0])
val week = validateInputStartDayOfWeek(i[1])
return month to week
}

fun validateInputWorkforce(input: String): List<String> {
val workforce = input.replace(" ", "").split(",")
validateWorkForceSize(workforce)
validateInputWorkersNameLength(workforce)
validateIdenticalName(workforce)
return workforce
}

private fun validateWorkForceSize(workforce: List<String>) {
require(workforce.size in 5..35) { ERROR_MSG_OUT_OF_RANGE_IN_WORKERS }
}

private fun validateInputMonth(input: String): Int {
val month = requireNotNull(input.toIntOrNull()) {
ERROR_MSG_CAN_INPUT_ONLY_NUMBER
}
require(month in 1..12) {
ERROR_MSG_OUT_OF_RANGE_IN_MONTH
}
return month
}

private fun validateInputStartDayOfWeek(input: String) =
requireNotNull(Week.entries.find { it.dayName == input }) {
ERROR_MSG_OUT_OF_RANGE_IN_WEEK_OF_DAY
}

private fun validateInputWorkersNameLength(workforce: List<String>) {
workforce.forEach { name ->
require(name.length <= 5) {
ERROR_MSG_CAN_INPUT_FIVE_LENGTH
}
}
}

private fun validateIdenticalName(workForce: List<String>) {
val set = workForce.toSet()
require(set.size == workForce.size) { ERROR_MSG_CANNOT_INPUT_DUPLICATE_NAME }
}

//모든 구성원의 근무가 평일, 휴일 모두 들어가 있는지 확인
fun validateWeekAndWeekendScehdule(weekWorkforce: Set<String>, weekendWorkforce: Set<String>) {
require(weekWorkforce == weekendWorkforce) { ERROR_MSG_NOT_IDENTICAL_WORKERS_SET }
}

companion object {
const val ERROR_MSG_OUT_OF_RANGE_IN_WORKERS = "[ERROR] 근무자는 5명에서 35명으로 작성해야합니다."
const val ERROR_MSG_CAN_INPUT_ONLY_NUMBER = "[ERROR] 월은 숫자만 입력이 가능합니다."
const val ERROR_MSG_OUT_OF_RANGE_IN_MONTH = "[ERROR] 1~12까지의 월 입력이 가능합니다."
const val ERROR_MSG_OUT_OF_RANGE_IN_WEEK_OF_DAY = "[ERROR] 일, 월, 화, 수, 목, 금, 토 요일 입력이 가능합니다."
const val ERROR_MSG_CAN_INPUT_FIVE_LENGTH = "[ERROR] 이름은 최대 5자리까지 가능합니다."
const val ERROR_MSG_CANNOT_INPUT_DUPLICATE_NAME = "[ERROR] 중복된 닉네임은 불가합니다."
const val ERROR_MSG_NOT_IDENTICAL_WORKERS_SET = "[ERROR] 모든 근무자는 평일, 휴일 모두 근무해야합니다"


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

fun <T>retryInput(input:() -> T): T {
while (true) {
try {
return input()
} catch (e: Exception) {
println("Error: ${e.message}")
}
}
}
27 changes: 27 additions & 0 deletions src/main/kotlin/oncall/view/InputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package oncall.view

import camp.nextstep.edu.missionutils.Console

class InputView {

fun inputMonthAndStartDayOfWeek(): String {
print(INPUT_MONTH_AND_START_DAY_OF_WEEK)
return Console.readLine()
}

fun inputWeekdaysWorkforce(): String {
print(INPUT_WEEKDAYS_WORKFORCE)
return Console.readLine()
}

fun inputWeekendsWorkforce(): String {
print(INPUT_WEEKENDS_WORKFORCE)
return Console.readLine()
}

companion object {
const val INPUT_MONTH_AND_START_DAY_OF_WEEK = "비상 근무를 배정할 월과 시작 요일을 입력하세요> "
const val INPUT_WEEKDAYS_WORKFORCE = "평일 비상 근무 순번대로 사원 닉네임을 입력하세요> "
const val INPUT_WEEKENDS_WORKFORCE = "주말 비상 근무 순번대로 사원 닉네임을 입력하세요> "
}
}
13 changes: 13 additions & 0 deletions src/main/kotlin/oncall/view/OutputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package oncall.view

import oncall.model.OnCallWorkSchedule

class OutputView {
fun printOnCallWorkSchedule(onCallWorkerSchedule: OnCallWorkSchedule) {
onCallWorkerSchedule.getResult().forEach {
//5월 5일 금(휴일) 루루
val holidayMark = if (it.isHoliday) "(휴일)" else ""
println("${it.month}월 ${it.date}일 ${it.dayOfWeek.dayName}${holidayMark} ${it.worker?.name}")
}
}
}
Loading