선택 구현 기능
- Lv4 : AddOperation(더하기), SubtractOperation(빼기), MultiplyOperation(곱하기), DivideOperation(나누기) 연산 클래스들을 AbstractOperation라는 클래스명으로 만들어 사용하여 추상화하고 Calculator 클래스의 내부 코드를 변경합니다.
- Lv3 와 비교해서 어떠한 점이 개선 되었는지 스스로 생각해 봅니다.
- hint. 클래스간의 결합도, 의존성(의존성역전원칙)
- Lv3 와 비교해서 어떠한 점이 개선 되었는지 스스로 생각해 봅니다.
//main 안에 op 입력 구현 부분
fun isValidOperator() : AbstractOperation {
while (true) {
try {
var op = readLine()!!.toInt()
if (op in 1..4) {
when(op) {
1 -> return AddOperation()
2 -> return SubstractOperation()
3 -> return MultiflyOperation()
4 -> return DivideOperation()
}
}
else println("1부터 4까지 연산자에 해당하는 숫자를 입력하세요")
} catch (e: NumberFormatException) {
println("입력값 오류! 숫자를 입력해주세요.")
}
}
}
기존에는 op의 타입과 isValidOperator()의 반환 타입이 Double이었지만 지금은 AbstractOperation이라는 추상 클래스로 바뀐 점이 주목할 부분이다.
class Calculator {
fun calculator(num1: Double, num2: Double, op: AbstractOperation): Double {
return when (op) {
is AddOperation -> {op.operation(num1,num2)}
is SubstractOperation -> {op.operation(num1,num2)}
is MultiflyOperation -> {op.operation(num1,num2)}
is DivideOperation -> {op.operation(num1,num2)}
else -> throw IllegalArgumentException("잘못된 연산자 입력값입니다.")
}
}
}
Operation 클래스들을 초기화해서 호출 하는 부분이 전부 날아갔다. op에 들어오는 클래스에 따라 그 클래스에 맞는 operation을 진행해준다. is 연산자를 통해 자식 클래스인 연산클래스들을 다운캐스팅해주어야 한다.
abstract class AbstractOperation {
abstract fun operation(num1:Double, num2:Double) : Double
}
class AddOperation : AbstractOperation() {
override fun operation(num1:Double, num2:Double) :Double = (num1 + num2)
}
class SubstractOperation : AbstractOperation() {
override fun operation(num1:Double, num2:Double) :Double = (num1 - num2)
}
class MultiflyOperation : AbstractOperation() {
override fun operation(num1:Double, num2:Double) :Double = (num1 * num2)
}
class DivideOperation : AbstractOperation() {
override fun operation(num1:Double, num2:Double) :Double = (num1 / num2)
}
들어가기에 앞서 이번 과제의 목표는 의존성 역전 원칙( Dependency Injectoin Principle ) 을 이해하는 것이다.
의존성 역전 원칙이란 객체는 저수준 모듈 보다 고수준 모듈에 의존해야 한다는 것이다.
지금 이해를 토대로 간단히 말하자면, 변하기 쉬운 코드에 의존하는 것(저수준 모듈 : 메인클래스, 객체 등)이 아닌 거의 변하지 않는 개념(고수준 모듈: 추상 클래스, 인터페이스)에 의존해야 한다는 것이다.
더 쉽게 말해보자면, 객체의 상속은 인터페이스 또는 추상 클래스을 통해 이루어져야 한다.
즉, 그냥 클래스 직접 상속받지 마라!! 인터페이스나 추상 클래스 만들어서 상속받아라!! 이다.
그렇다면 왜 그렇게 해야하느냐??
지금 하고 있는 계산기를 예시로 생각해보자
abstract class AbstractOperation 추상클래스 그리고 그 안에 abstract fun operation 추상 메소드를 만들어 줌으로써 이 클래스를 상속받는 모든 클래스들은 미완성된 추상 메소드를 오버라이딩하여 기능을 완성하게끔 강제한다.
이를 통해 클래스간의 조직화가 가능해졌고 존재의 이유를 명확하게 할 수 있다. 또한 다른 기능의 클래스(예를 들어 제곱을 해주는 연산)가 생겼을 때 추가적으로 수정해야할 부분은 calculator 함수에 밑에 코드 한 줄이면 된다.
is SquareOperation -> {op.operation(num1,num2)}
변경 사항에 대처하는 것이 유연해져서 유지 보수가 간결해지고 확장성이 용이하다.
'MyProject' 카테고리의 다른 글
개인 과제 계산기 만들기 Lv4 Upgraded (0) | 2023.11.30 |
---|---|
개인 과제 계산기 만들기 Lv3 (0) | 2023.11.29 |
개인 과제 계산기 만들기 Lv2 (0) | 2023.11.29 |
개인 과제 계산기 만들기 Lv1 (0) | 2023.11.28 |