본문 바로가기
카테고리 없음

TIL - 23.12.08

by JHBang 2023. 12. 8.

오늘은 어제까지 진행했던 과제 코드를 제출했다.

그리고 얼마 지나지 않아 과제 해설 영상이 올라왔다.

 

과제 해설 영상에선 생각보다 간단하게 코드를 구성했다.

 

fun main(){
    // 더하기
    val addCalc = Calculator(AddOperation())
    println("10 더하기 20 결과는 : ${addCalc.operate(10, 20)}입니다.")
    // 빼기
    val subsCalc = Calculator(SubstractOperation())
    println("10 빼기 20 결과는 : ${subsCalc.operate(10, 20)}입니다.")
    // 곱하기
    val mulCalc = Calculator(MultiplyOperation())
    println("10 곱하기 20 결과는 : ${mulCalc.operate(10, 20)}입니다.")
    // 나누기
    val divCalc = Calculator(DivideOperation())
    println("10 나누기 20 결과는 : ${divCalc.operate(10, 20)}입니다.")
}
class Calculator(private val operator: AbstractOperation) {
    fun operate(num1: Int, num2: Int): Double{
        return operator.operate(num1, num2)
    }
}
abstract class AbstractOperation {
    abstract fun operate(num1: Int, num2: Int): Double
}
class AddOperation : AbstractOperation(){
    override fun operate(num1: Int, num2: Int): Double = (num1 + num2).toDouble()
}
// 뺄셈, 곱셈, 나눗셈도 같은 형태

 

기본적인 구조는 main함수에 Calculator 클래스를 각각의 연산에 맞게 인스턴스화 한다.

 

Calculator클래스의 객체를 생성할 때 매개변수로 연산을 수행하는 클래스(AddOperator같은 클래스)를 준다.

 

Calculator클래스는 주 생성자로 추상화 클래스가 생성되어있다. Calculator 클래스 내부 메소드에서는 주 생성자로 선언한 추상화클래스의 메소드를 불러온다.

 

추상화 클래스는  추상화 메소드를 생성한 상태이고, 이 메소드는 자식 클래스인 연산 클래스들이 오버라이딩 한다.

 

즉 main함수에서 Calculator클래스에 매개변수로 연산 클래스를 주게 되면, 추상화 클래스가 매개변수로 선언된 Calculator클래스가 작동할 수 있게 되는 것이다. 연산 클래스는 추상화 클래스에 상속된 상태이기 때문이다.

 

이런식으로 오버라이딩 된 추상화 클래스의 메소드가 Calculator클래스에 의해 사용되면서 계산이 이루어지는 것이다.

 

 

일단 내가 짠 코드와 비교했을 때 기능은 내 코드가 더 많았다. 사실 코드를 짜다보니 욕심이 생겨 숫자도 선택할수 있게 해 보고, 연산도 선택할 수 있게 해 봤다.

 

구조적인 차이점도 존재했다. 아래는 내가 짠 lv4 코드의 Calculator클래스다.

class Lv4Calculate() {
    lateinit var operation: Lv4AbstractOperation
    fun operate(num1: Int, num2: Int): Double {
        return operation.operate(num1, num2)
    }
}

나는 주 생성자를 이용하지 않고 클래스 내부에 추상화 클래스 객체를 선언했다. 그리고 지연 초기화를 위해 lateinit 도 사용했다.

fun lv4(num1: Int, num2: Int){
    val lv4class = Lv4Calculate2()
    var abstractClass: Lv4AbstractOperation ?= null
    print("Choose operator(+, -, *, /): ")
    val operator: String = readln()
    when(operator){
        "+" -> abstractClass = Lv4AddOperation()
        "-" -> abstractClass = Lv4SubstractOperation()
        "*" -> abstractClass = Lv4MultiflyOperation()
        "/" -> abstractClass = Lv4DivideOperation()
        else -> println("Wrong Operator.")
    }
    if (abstractClass != null) {
        lv4class.operation = abstractClass
        println("$num1 $operator $num2 = ${lv4class.operate(num1, num2)}")
    }
}

main함수에서도 차이점이 존재했다.

난 추상화클래스를 직접적으로 객체화했고, 추상화 클래스 객체를 선택에 맞게 초기화해주는 방식으로 갔다.

 

그런데 인터넷을 찾아보니 추상화 클래스는 보통 직접적으로 객체화하지 않는다고 한다....

 

그래서 calculator 클래스와 main클래스를 해설 동영상 방식으로 수정해 봤다.

class Lv4Calculate(var operation: Lv4AbstractOperation) {
    fun operate(num1: Int, num2: Int): Double {
        return operation.operate(num1, num2)
    }
}
fun lv4(num1: Int, num2: Int){
    var abstractClass: Lv4AbstractOperation ?= null

    print("Choose operator(+, -, *, /): ")
    val operator: String = readln()
    when(operator){
        "+" -> abstractClass = Lv4AddOperation()
        "-" -> abstractClass = Lv4SubstractOperation()
        "*" -> abstractClass = Lv4MultiflyOperation()
        "/" -> abstractClass = Lv4DivideOperation()
        else -> println("Wrong Operator.")
    }
    val lv4class = abstractClass?.let { Lv4Calculate(it) }
    if (abstractClass != null) {
        if (lv4class != null) {
            lv4class.operation = abstractClass as Lv4AbstractOperation
        }
        if (lv4class != null) {
            println("$num1 $operator $num2 = ${lv4class.operate(num1, num2)}")
        }
    }

 

원래 추상화 클래스를 객체화 하는걸 피하려 했지만 연산자 선택을 위해 남겨두기로 했다.

 

    val lv4class = abstractClass?.let { Lv4Calculate(it) }은 abstractClass가 null이 아닐 경우 람다함수가 실행되는 코드이다.

 

이 후에도 null 세이프티로 코드를 완성했다.