오늘은 kotlin 3주차 후반, 4,5주차 강의까지 모두 수강했다.
원래라면 강의 내용을 이해하기 위해 천천히 곱씹어 보면서 이해하는 방식으로 했지만 앞으로 제출해야 할 과제도 있기 떄문에 일단 나머지 강의는 흐름을 이해하는 식으로 수강했다,
3주차 후반 내용은 class의 상속과 오버라이딩, 오버로딩, 인터페이스에 관한 내용이였다.
4, 5주차엔 접근 제한자, null 세이프티, 배열, 예외처리, 확장함수, 쓰레드, 코루틴에 관한 내용이였다.
강의 내용은 대체로 기본 구조만 알려주는 식이라 나중에 제대로 복습을 해야 할 필요성을 느꼈다.
오늘부터 금요일까지 제출해야 할 과제를 시작했다. 과제 내용은 계산기 구현이였는데, kotlin의 클래스가 어떤식으로 사용되는지, SOLID원칙이 어떤 식으로 적용되는지에 대한 이해를 돕기 위한 과제로 보였다. 아래는 과제 내용이다.
필수 구현 기능
- Lv1 : 더하기, 빼기, 나누기, 곱하기 연산을 수행할 수 있는 Calculator 클래스를 만들고, 클래스를 이용하여 연산을 진행하고 출력하기
- Lv2 : Lv1에서 만든 Calculator 클래스에 나머지 연산을 가능하도록 코드를 추가하고, 연산 진행 후 출력하기
- Lv3 : AddOperation(더하기), SubstractOperation(빼기), MultiplyOperation(곱하기), DivideOperation(나누기) 연산 클래스를을 만든 후 클래스간의 관계를 고려하여 Calculator 클래스와 관계를 맺기
- 관계를 맺은 후 필요하다면 Calculator 클래스의 내부코드를 변경하기
- 나머지 연산자(%) 기능은 제외합니다.
- Lv2 와 비교하여 어떠한 점이 개선 되었는지 스스로 생각해 봅니다.
- hint. 클래스의 책임(단일책임원칙)
- 관계를 맺은 후 필요하다면 Calculator 클래스의 내부코드를 변경하기
선택 구현 기능
- Lv4 : AddOperation(더하기), SubtractOperation(빼기), MultiplyOperation(곱하기), DivideOperation(나누기) 연산 클래스들을 AbstractOperation라는 클래스명으로 만들어 사용하여 추상화하고 Calculator 클래스의 내부 코드를 변경합니다.
- Lv3 와 비교해서 어떠한 점이 개선 되었는지 스스로 생각해 봅니다.
- hint. 클래스간의 결합도, 의존성(의존성역전원칙)'\
- Lv3 와 비교해서 어떠한 점이 개선 되었는지 스스로 생각해 봅니다.
과제를 진행하기 위해 안드로이드 스튜디오에 새 프로젝트를 생성했다.
그리고 테스트삼아 코드를 실행했는데 오류가 발생했다.
An issue was found when checking AAR metadata:
1. Dependency 'androidx.activity:activity:1.8.0' requires libraries and applications that
depend on it to compile against version 34 or later of the
Android APIs.
:app is currently compiled against android-33.
Recommended action: Update this project to use a newer compileSdk
of at least 34, for example 34.
Note that updating a library or application's compileSdk (which
allows newer APIs to be used) can be done separately from updating
targetSdk (which opts the app in to new runtime behavior) and
minSdk (which determines which devices the app can be installed
on).
해당 코드는 컴파일러의 버젼 문제로 발생한 오류였다.
이 전에 공부용으로 만들어놓은 프로젝트는 이런 오류가 안 떴었는데, 새로 만들고 나니 발생한 오류였다.
구글에 검색해서 찾던 중 스택 오버플로우에 나와 똑같은 오류가 발생한 사람의 질문을 찾을 수 있었고, 해결방법 또한 알게 됐다.
Gradle Script의 build.gradle.kts의 dependencies중 한 줄이 문제였다.
implementation("com.google.android.material:material:1.10.0")
버젼 정보가 1.10.0인데 이를 1.8.0으로 바꿔주면 되는 문제였다.
implementation("com.google.android.material:material:1.8.0")
혹시 다른 팀원들도 같은 문제를 겪진 않았을까 해서 해당 문제를 팀원과 공유했다.
그런데 팀원들은 안드로이드 스튜디오 말고 인텔리제이 IDE를 사용하고 있었다.
알고보니 현직자였던 팀원분이 인텔리제이가 더 강력한 기능을 많이 제공하고 있기 떄문에 어플 개발이 아닌 이상 보통
인텔리제이를 사용한다고 알려주셨다.
인텔리제이는 jetbrain에서 만든 IDE인데, 여기서 만든 IDE중 나한테 익숙한 파이참도 있었다. 그렇다보니 인텔리제이를
사용하는게 나한테도 더 익숙한 UI를 제공했다.
IDE도 바꿨으니 본격적으로 lv1 기능부터 차례대로 진행했다.
class Lv1Calculate(val num1: Int, val num2: Int) {
fun addNum() {
val sumNum = num1+num2
println("$num1 + $num2 = $sumNum")
}
fun subsNum() {
val subsNum = num1-num2
println("$num1 - $num2 = $subsNum")
}
fun multiNum() {
val multiNum = num1*num2
println("$num1 x $num2 = $multiNum")
}
fun divNum() {
val divNum = num1.toFloat()/num2
println("$num1 / $num2 = $divNum")
}
}
fun main() {
fun lv1(num1: Int, num2: Int) {
var lv1Class = Lv1Calculate(num1, num2)
print("Choose operator(+, -, *, /): ")
var operator: String = readln()
if(operator == "+")
lv1Class.addNum()
else if(operator == "-")
lv1Class.subsNum()
else if(operator == "*")
lv1Class.multiNum()
else if(operator == "/")
lv1Class.divNum()
else{
println("Wrong Operator.")
return
}
}
fun lv2(num1: Int, num2: Int) {
...
}
}
fun lv3(num1: Int, num2: Int){
...
}
}
print("num1: ")
var num1 = readln().toInt()
print("num2: ")
var num2 = readln().toInt()
print("Choose level (1 ~ 4, 0 is exit): ")
var choice = readln().toInt()
when (choice) {
1 -> lv1(num1, num2)
2 -> lv2(num1, num2)
3 -> lv3(num1, num2)
0 -> return
else -> print("wrong choice.")
}
}
숫자 2개를 사용자에게 받아서 원하는 레벨을 선택하고 레벨에 맞는 클래스를 실행시키는 구조로 작성했다.
fun lv1에서 Lv1Calculate클래스를 인스턴스화 하고 연산자를 입력받아 클래스 내부 메소드를 이용한다.
lv2도 나머지 연산 추가만 하면 끝나기 때문에 빠르게 끝낼 수 있었다.
fun lv2(num1: Int, num2: Int) {
var lv2Class = Lv2Calculate(num1, num2)
print("Choose operator(+, -, *, /, %): ")
var operator: String = readln()
if(operator == "+")
lv2Class.addNum()
else if(operator == "-")
lv2Class.subsNum()
else if(operator == "*")
lv2Class.multiNum()
else if(operator == "/")
lv2Class.divNum()
else if(operator == "%")
lv2Class.remainderNum()
else{
println("Wrong Operator.")
return
}
}
class Lv2Calculate(val num1: Int, val num2: Int) {
fun addNum() {
val sumNum = num1+num2
println("$num1 + $num2 = $sumNum")
}
fun subsNum() {
val subsNum = num1-num2
println("$num1 - $num2 = $subsNum")
}
fun multiNum() {
val multiNum = num1*num2
println("$num1 x $num2 = $multiNum")
}
fun divNum() {
val divNum = num1.toFloat()/num2
println("$num1 / $num2 = $divNum")
}
fun remainderNum() {
val remainderNum = num1 % num2
println("$num1 % $num2 = $remainderNum")
}
}
lv3부터는 문제가 좀 난해했는데, 클래스간 관계를 맺으라는게 무슨 소리인지 이해하기 힘들었다.
과제 설명 아래에 예제 코드가 있어서 lv3 예제 코드를 참고했다.
lv3 예제
main.kt
fun main() {
val calc = Calculator()
println("1 더하기 2 결과는 : ${calc.addOperation(AddOperation(), 1, 2)} 입니다")
}
AddOperation.kt
class AddOperation{
fun operate(num1: Int, num2: Int): Double = (num1 + num2).toDouble()
}
Calculator.kt
class Calculator {
fun addOperation(##답안##): Double {
return ##답안##
}
}
예제를 확인해 보니 클래스 객체를 매개변수로 받아서 사용하는 형태였다. 또한 단일책임원칙이라는 키워드도 문제 안에 있었다.
단일 책임 원칙(SRP)은 SOLID 원칙 중 하나이며
"하나의 클래스가 수정되기 위해서는 오직 하나의 이유여야만 한다."
라는 개념이다. 이해한 바로는 책임이란 수정될 가능성을 말하는 것이고, 하나의 클래스에서 모든 작업을 처리하는 메소드가 존재하면 유지보수와 재사용성이 떨어진다는 것이다.
때문에 위 예제에서 클래스 객체를 매개변수로 사용한다는 말은 계산기 클래스의 메소드에 실제 계산 부분을 다른 클래스로 빼서 구현하라는 의미라고 생각했다.
아래는 내가 구현한 코드다.
class Lv3AddOperation {
fun operate(num1: Int, num2: Int)= (num1 + num2)
}
(Substract, Multiply, Divide도 같은 식으로 클래스 생성)
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")
}
}
Lv3Calculator 클래스는 주 생성자로 각각의 계산을 담당하는 클래스를 받는다. 이 객체는 계산기의 메소드에서 사용된다.
여기서 계산기 클래스는 아무 값도 받지 않아도 된다.
만약 이 계산기 클래스 Lv3Calculator을 10명이 사용한다고 해 보자. 더하기 부분에 수정사항이 생겼다고 가정했을 때
lv1이나 lv2 코드에선 10명의 컴퓨터에 모두 수정사항을 기입해야 한다.
하지만 lv3에선 더하기를 구현한 Lv3AddOperation클래스에 수정할 일이 생겨도 해당 클래스 하나만 수정하면 계산기 클래스 사용자 모두에게 적용된다.
lv4는 내일 더 깊게 파고들어 볼 예정이다.
추상화에 관한 공부는 lv3을 공부하면서 어느정도 찾아봤기 때문에 어렵지 않게 구현할 수 있을 것이다.
'캠프 개발일지' 카테고리의 다른 글
샤ㅣ (0) | 2024.01.16 |
---|---|
TIL - 23.12.11 (0) | 2023.12.12 |
TIL - 23.12.05 (2) | 2023.12.05 |
TIL - 23.12.04 (0) | 2023.12.04 |
TIL = 23.12.01 (0) | 2023.12.01 |