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

TIL - 23.12.07

by JHBang 2023. 12. 7.

오늘은 오전에 학교를 다녀온 탓에 코딩할 시간이 평소보다 적었다...

 

그래도 내일이 과제 제출일이므로 마저 하던 과제를 끝내고자 했다.

 

lv4 파트를 구현했다.

 

  • Lv4 : AddOperation(더하기), SubtractOperation(빼기), MultiplyOperation(곱하기), DivideOperation(나누기) 연산 클래스들을 AbstractOperation라는 클래스명으로 만들어 사용하여 추상화하고 Calculator 클래스의 내부 코드를 변경합니다.
    • Lv3 와 비교해서 어떠한 점이 개선 되었는지 스스로 생각해 봅니다.
      • hint. 클래스간의 결합도, 의존성(의존성역전원칙)
      •  

lv4는 추상화 클래스를 이용하는 코드를 작성하는 문제였다.

 

추상화 클래스는 찾아보니 강의때 들은 인터페이스와 유사했다. 

 

두 기능으로 만든 클래스를 고수준 모듈이라고 하고, 일반적으로 구현된 객체를 저수준 모듈이라고 한다.

 

의존성역전원칙은 "객체는 저수준 모듈보다 고수준 모듈에 의존해야 한다" 는 원칙이다.

 

즉 객체는 객체보다 인터페이스나 추상화 클래스에 의존해야 한다는 말이다.

 

난 이 말을 객체의 상속은 추상이나 인터페이스를 통해 이루어져햐 한다는 의미로 이해했다.

 

계산기 코드에 적용하기 위해 그림을 조금 그려봤다.

 

Lv3의 계산기 클래스

class Lv3Calculator(
    private val addOperation: Lv3AddOperation,
    private val subsOperation: Lv3SubstractOperation,
    private val multiOperation: Lv3MultiplyOperation,
    private val divOperation: Lv3DivideOperation
) {
    fun addNum(num1: Int, num2: Int) {
        var sumNum = addOperation.operate(num1, num2)
        println("$num1 + $num2 = $sumNum")
    }
    fun subsNum(num1: Int, num2: Int) {
        var subsNum = subsOperation.operate(num1, num2)
        println("$num1 - $num2 = $subsNum")
    }
    fun multiNum(num1: Int, num2: Int) {
        val multiNum = multiOperation.operate(num1, num2)
        println("$num1 x $num2 = $multiNum")
    }
    fun divNum(num1: Int, num2: Int) {
        val divNum = divOperation.operate(num1, num2)
        println("$num1 / $num2 = $divNum")
    }
}
class Lv4AddOperation: Lv4AbstractOperation() {
    override fun operate(num1: Int, num2: Int): Double= (num1 + num2).toDouble()
}
// 뺄셈, 곱셈, 나눗셈도 같은 형태

Lv3 계산기 클래스는 각각의 연산용 클래스를 매개변수로 받아 클래스 내부 메소드만으로 계산을 구현한다. 만약 이 계산기에 다른 연산기능을 추가하고자 한다면 어떻게 될까?

 

빨간색 ? 기능을 넣는다고 하면 계산기 클래스 내부에 ?기능을 담당하는 메소드를 하나 더 만들어야 할 것이다.

 

하지만 추상화를 사용한다면 이렇게 바뀐다.

 

class Lv4Calculate() {
    lateinit var operation: Lv4AbstractOperation
    fun operate(num1: Int, num2: Int): Double {
        return operation.operate(num1, num2)
    }
}
abstract class Lv4AbstractOperation {
    abstract fun operate(num1: Int, num2: Int): Double
}
class Lv4AddOperation: Lv4AbstractOperation() {
    override fun operate(num1: Int, num2: Int): Double= (num1 + num2).toDouble()
}
// 뺄셈, 곱셈, 나눗셈도 같은 형태

 

추상화 클래스는 아직 정의가 안된 메소드를 가지고 있으며, 연산 클래스는 추상화 클래스를 상속받아 추상화 클래스의 메소드를 오버라이딩 한다.

 

계산기 클래스에선 추상화 클래스를 지연 초기화 한다. 그리고 숫자를 받아 추상화 클래스의 메소드를 실행한다.

 

main 문에선 이렇게 구성했다.

fun lv4(num1: Int, num2: Int){
    val lv4class = Lv4Calculate()
    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)}")
    }
}

abstractClass라는 객체를 추상화 클래스 형태로 선언하고 null 인지 확인한다.

 

사용자에게 연산자를 입력받으면 해당 연산자에 맞는 클래스를 추상화클래스 객체 abstractClass에 넣는다.

 

계산기 클래스의 opration 부분에 abstractClass객체를 넣으면 operate 메소드를 실행할 수 있다.

 

이런식으로 코드를 짜니 일단 실행은 잘 됐다. 하지만 내가 짠 코드가 진짜로 의존성 역전 원칙을 따르는 코드인지 확인을 할수 없었다. 내일 튜터님께 여쭤봐야겠다.