오늘 계획
⏲️ 공부시간 : 09 : 10 ~ 09 : 25
오늘은 문제가 쉬워서 보고 바로 풀었당.
근데 역시 다른 사람 답안을 보면 항상 나보다 훨씬 간결하게 푼 사람들이 있어버림~!
나도 빨리 저렇게 문제보면 바로 제일 효율적인 방식을 떠올려서 풀고싶당
⏲️ 공부시간 : 09 : 40 ~
너무 많아서 뭐부터 건드려야할지 어지럽다~
그래도 기능적인게 우선인거 같으니까!
괄호랑 연산시 우선순위를 알아서 파악하는 기능을 먼저 추가해야겠다
지금 괄호 버튼은 있는데 얘는 아직 동작을 지정안해줬음
앞에 괄호가 없거나 닫혀있으면, 여는 괄호가 , 앞에 괄호가 열려있으면 닫는 괄호가 입력되게 해줘야함
private fun addParenthesisInput(){
val actualInput = if (isNextParenthesisOpen(currentInput)){
"("
}else {
")"
}
currentInput += actualInput
displayText.text = currentInput
}
만약 괄호가 열려있으면 닫힌괄호가 , 닫혀있으면 열린괄호가 나오도록 해줌
private fun isNextParenthesisOpen(currentInput: String) : Boolean {
val openCount = currentInput.count { it == '(' }
val closeCount = currentInput.count { it == ')' }
return openCount <= closeCount
}
여기서 괄호가 열렸는지 닫혔는지를 판단해줌
( 개수가 더 많으면 열려있는거고 괄호개수가 같으면 닫혀있는거임
binding.ibtnParenthesis.setOnClickListener {
playSound()
addParenthesisInput()
}
괄호 버튼을 눌렀을 때 어제 넣은 효과음재생이랑 방금 만든 함수가 실행되게 해줌
메인 액티비티가 너무 어지러워서 효과음은 클래스로 분리시켜줬다.
package com.example.secondweek
import android.content.Context
import android.media.MediaPlayer
class Sound(private val context : Context) {
private var mediaPlayer: MediaPlayer = MediaPlayer.create(context, R.raw.vintage)
fun playSound() {
if (mediaPlayer.isPlaying) {
mediaPlayer.stop()
mediaPlayer.release()
mediaPlayer = MediaPlayer.create(context, R.raw.vintage)
}
mediaPlayer.start()
}
fun release() {
mediaPlayer.release()
}
}
private lateinit var sound: Sound
//중간 생략 //
sound = Sound(this)
for (button in numberButtonList) {
button.setOnClickListener {
**sound.playSound()**
addCurrentInput(button.contentDescription.toString())
}
}
다음으로 계산 우선순위 구현하려고 머리싸매고 고민하고 방법찾아봐도 어떻게 해야할지 감이안왔당..
어떤 일이 막힐 때는 일단.. 멈춰서 지금까지 한거 점검을 한번 하고 넘어가면서 리프레쉬도하고해야하기때문엥 튜터님한테 찾아가서 지금까지 진행한 거에서 문제가없는지 여쭤봤음!
사실 내가 코딩잘하지도 못하면서 혼자서 종영한지 몇년된 무한도전 찍고있는 격이라서 민망해서 튜터님 찾아가면 어려우시면 이렇게하지말고 그냥 원래 과제대로 콘솔로 실행하는 계산기나 만드세요하실까봐 혼자서 머리 싸매고있었는데 살짝 지금쯤 찾아가면 도저히 무르라고 못하시겠지..하고 갔음 ㅎ.ㅎ
튜터님께 일단 지금까지 한 과제 내용이 올바르게 진행한건지 여쭤봤는데,
lv 4 에서는 의존성 역전 법칙을 활용해야 할것같다고 추상클래스를 활용하긴했지만, 의존성 역전법칙이 없다고(?) 알려주심!
어떻게 해야할지 알려주시긴했는데 의존성 역전법칙에 대해 아직 모르는상태로 할수는 없으니까 먼저 찾아봐야겠다.
DIP ( Dependency Inversion Principle)
고수준 모듈이 저수준 모듈에 의존해서는 안되고, 둘 다 추상화에 의존해야한다는 원칙
의존 관계를 인터페이스나 추상 클래스를 활용해서 역전 시키는것 을 의미
의존성 역전 법칙에서 중요한것
의존이 뭔데 슈발? 뭐가 고수준 모듈이고 뭐가 저수준 모듈인데 왜 수준따지는데! 세상에귀천이어딨는데!
라고 하면안되고 공부해야겠지용~!
프로그래밍에서의 의존한다는것?
한 클래스나 모듈이 다른 클래스나 모듈의 기능에 의존하는 관계
클래스 A가 클래스 B 에 의존하는 경우
클래스 의존성
클래스 A가 클래스 B의 객체를 생성/ B의 메서드를 호출
인터페이스 의존성
클래스 A가 인터페이스 I를 사용하고, 클래스 B가 I를 구현하는 경우 A가 I 에 의존한다.
생성자 의존성 주입
클래스 A가 필요한 객체를 생성자에서 주입 받는 경우
메서드 의존성 주입
클래스 A가 필요한 객체를 메서드에서 주입 받는 경우
필드 의존성 주입
클래스 A가 필요한 객체를 필드에서 주입 받는 경우
고수준 모듈 : 실제로 로직을 사용하는 클래스
저수준 모듈 : 실제 연산을 수행하는 클래스
그러면 고수준 저수준은 한 프로젝트 내에서 절대적인건가?
→아니라고함 고수준 저수준모듈의 구분은 상대적이다.
흠 아무튼 쉽게 정리하면 고수준 모듈에서 다른 클래스의 무언가를 갖고와서 사용하려면 그 클래스를 추상화해서 사용해야하는거 같음~~
튜터님에게 피드백 받은대로 Lv 4 를 온전히 수행하기 위해서 수정을 했다.
고수준 모듈이 저수준 모듈에 의존하지 않도록 수정해야했던 부분들은 Calculator 클래스와 MainActivity 엿는데
Calculator 클래스 코드 수정 전
open class Calculator {
private val operations = mapOf<String, AbstractOperation>(
"+" to AddOperation(),
"-" to SubstractOperation(),
"*" to MultiplyOperation(),
"/" to DivideOperation(),
"%" to ModOperation()
)
fun calculate(numbers: List<Number>, operator : String): Number {
val operation = operations[operator] ?: throw IllegalArgumentException ("invalid operator")
return operation.apply(numbers)
}
}
Calculator 클래스 코드 수정 후
open class Calculator(private val operations : Map <Char, AbstractOperation>) {
fun calculate(numbers: List<Number>, operators: List<Char>): Number {
val result = numbers[0]
for (i in operators.indices) {
val nextNumber = numbers[i + 1]
val currentNumbers = listOf(result, nextNumber)
val operation =
operations[operators[i]] ?: throw IllegalArgumentException("invalid operator")
return operation.apply(currentNumbers)
}
return result
}
}
MainActivity 코드 수정 전
private val calculator = Calculator()
MainActivity 코드 수정 후
private lateinit var calculator : Calculator // calculator 라는 변수를 생성할건데 나중에 초기화할거고, Calculator 클래스의 인스턴스를 할당하겠다
val operations = mapOf(
'+' to AddOperation(),
'-' to SubstractOperation(),
'*' to MultiplyOperation(),
'/' to DivideOperation(),
'%' to ModOperation()
) // operations 라는 변수에 연산자와 추상 클래스를 구현한 연산클래스를 맵핑
calculator = Calculator(operations) // calculator 변수를 Calculator에 operations를 넣은 객체로 초기화
MainActivity calculateResult 코드 수정 전
private fun calculateResult() {
val numbers = stringAnalyze.filterNumbers(currentInput)
val operators = stringAnalyze.filterOperators(currentInput)
if (numbers.isEmpty() || operators.isEmpty()){
displayText.text = "there's no number"
return
}
var calculateResult: Number = numbers[0]
for (i in operators.indices) {
val nextNumber = numbers[i+1]
val currentNumbers = listOf(calculateResult, nextNumber)
calculateResult = calculator.calculate(currentNumbers, operators[i])
}
val result = calculateResult.toString().removeSurrounding("[","]")
displayText.text = result
currentInput = result
}
MainActivity calculateResult() 코드 수정 후
private fun calculateResult() {
val numbers = stringAnalyze.filterNumbers(currentInput)
val operators = stringAnalyze.filterOperators(currentInput)
if (numbers.isEmpty() || operators.isEmpty()) {
displayText.text = "there's no number"
return
}
val calculateResult = calculator.calculate(numbers, operators.map { it[0] })
val result = calculateResult.toString()
displayText.text = result
currentInput = result
}
Calculator 클래스는 원래 매개변수가 따로 없고,
operations 라는 변수에 연산자랑 연산자에 맞는 연산함수를 맵핑해주고,
calculate 라는 함수에 게산기에 입력한 문자열을 StringAnalyze 라는 클래스에서 숫자랑 연산자로 나눠 준 리스트를 받아서 연산해줬었음
의존성 역전 법칙을 적용해야하기 때문에
근데 이제 Calculator 클래스에 매개변수로 Char, AbstractOperation 의 Map을 받도록 해주면서
calculator 클래스가 연산 클래스에 직접 의존하지 않고, 연산을 추상화해서 처리하도록 해줬다.
MainActivityoperations 라는 맵을 생성해서, Calculator 클래스의 인스턴스를 생성할때 주입하도록 해줬다.
더 공부하고 정리했어야하는데
같은 팀원 분 중에 비전공자 분이 계신데 과제 이해를 잘못하고계셨던걸 이제야 알아채버려서
내가 팀장인데 너무 죄송했당 ㅜ 내가 잘 챙겨드렸어야했는데 지금 금요일인데 너무 늦게알아차렸잖아요..
섬세하게 신경썼으면 알아챌만도 했을 거같아서 너무 죄송했음 ㅜㅜ
암튼 그래서 팀원분한테 과제 관련해서 조금이라도 알려드리다가 거기서 또 다른 분들이 오셔서
수다를 잔뜩 떨어버리는 바람에 마무리를 못함 근데 내일 글 정리해서 올릴까 하다가 걍 올릴랭~
내일은 내일의 할일이 많기때문에
주말에는.. 나도 콘솔로만 구현하는 것도 그냥 혼자 만들어봐야겠음!
앱으로 만들기만 해서는 혹시 콘솔로 하는 것 중에 놓치는게 있을 수 도있을 것같당~~