꿈틀꿈틀 개발일기

20231208 / TIL / 계산기 만들기 - 개인과제

by jeongminy

코드카타

# 코드타카 11번 문제를 풀었다. / 짝수와 홀수

class Solution {
    fun solution(num: Int): String {
        var answer = num%2
        if (answer == 0){
            return "Even"
        } else {
            return "Odd"
        }
    }
}

# 코드타카 12번 문제를 풀었다. / 평균 구하기

class Solution {
    fun solution(arr: IntArray): Double {
        return arr.average()
    }
}

# 코드타카 13번 문제를 풀었다. / 자릿수 더하기

class Solution {
    fun solution(n: Int): Int {
        val x = n.toString()
        var sum = 0
        for (i in x) {
            sum += i.toString().toInt()
        }
        return sum
    }
}

 


개념 정리

#  어제 튜터님에게 배운 내용을 간략히 정리해봤다

 


Parameter 와 Property

# 혼동되는 개념을 정리해봤다

1. Parameter (매개변수)

  • 매개변수는 함수나 메서드에 전달되는 값으로, 함수가 호출될 때 전달되는 값이 해당 매개변수에 대입됩니다.
  • 함수나 메서드의 정의부에서 선언되며, 호출할 때마다 전달되는 값이 달라질 수 있습니다.
  • 예를 들어, 다음은 매개변수를 사용하는 함수의 예제입니다
fun add(a: Int, b: Int): Int {
    return a + b
}

여기서 ab는 매개변수로, 함수가 호출될 때마다 전달된 값으로 대체됩니다.

 

2. Property (속성)

  • 속성은 클래스나 객체의 멤버 변수로, 해당 클래스나 객체의 상태를 나타냅니다.
  • 클래스 내부에서 선언되며, 클래스의 인스턴스(객체)마다 각각의 값을 가질 수 있습니다.
  • 주로 클래스의 상태를 표현하거나, 데이터를 저장하는 데 사용됩니다.
  • 예를 들어, 다음은 속성을 사용하는 클래스의 예제입니다
class Person {
    var name: String = ""
    var age: Int = 0
}

여기서 nameage는 클래스 Person의 속성으로, 각각 문자열과 정수 값을 가질 수 있습니다.

간단하게 말하면,
Parameter(매개변수)는 함수에 전달되는 값
Property(속성)은 클래스나 객체의 상태를 나타내는 값

 


 

 

클래스의 속성(property)를 다르게 표현하는 방법

1. 프로퍼티 선언 시 초기화
프로퍼티를 선언하면서 동시에 초기화할 수 있습니다. 이때 초기화 값에 따라 타입 추론이 가능합니다.

class Person {
    var name: String = "John"
    var age: Int = 25
}

 

2. 생성자 매개변수로 초기화  (이 부분에서 파라미터와 프로퍼티가 혼동됬던것 같다^^;;)
클래스의 주 생성자를 사용하여 프로퍼티를 초기화할 수 있습니다.

class Person(val name: String, val age: Int)

이 경우 nameage읽기 전용(readonly) 프로퍼티가 됩니다.

 

3. 지연 초기화 (Late Initialization)
프로퍼티를 나중에 초기화하고자 할 때 lateinit 키워드를 사용할 수 있습니다.
단, lateinit는 var 프로퍼티에만 사용할 수 있습니다.

class Person {
    lateinit var name: String
    var age: Int = 0
}

 

4. 커스텀 Getter/Setter 사용 ( 아직 이해가 되지 않는다 ^^:; )

class Person {
    var name: String = ""
        get() = field.toUpperCase()
        set(value) {
            field = "Name: $value"
        }
}

이 예제에서는 name의 Getter에서는 값을 대문자로 변환하고, Setter에서는 입력된 값을 가공하여 저장합니다.

 

5. 초기화 블록 활용
init 블록을 사용하여 초기화 로직을 추가할 수 있습니다.
이 방법은 주로 생성자 로직이 복잡한 경우에 사용됩니다.

class Person {
    var name: String
    var age: Int

    init {
        name = "John"
        age = 25
    }
}

 

 

이러한 다양한 방법을 통해 Kotlin에서 클래스의 프로퍼티를 다르게 표현할 수 있습니다.
선택할 방법은 사용하려는 상황과 선호하는 코딩 스타일에 따라 다를 수 있습니다.

 

 

 

 interface 와 abstract

1. Interface (인터페이스)

  • 선언: interface 키워드를 사용하여 선언합니다.
  • 특징:
    • 추상 메서드(abstract method)와 프로퍼티를 정의할 수 있습니다.
    • 구현이 없는 메서드를 가질 수 있습니다.
    • 클래스는 여러 인터페이스를 구현할 수 있습니다.
    • 다중 상속을 지원합니다.
  • 예시
interface MyInterface {
    // 인터페이스 멤버들
    fun doSomething()
    val property: Int
}

 

2. Abstract (추상 클래스)

  • 선언: abstract 키워드를 사용하여 선언합니다.
  • 특징:
    • 추상 메서드와 일반 메서드를 모두 가질 수 있습니다.
    • 추상 메서드는 반드시 하위 클래스에서 구현되어야 합니다.
    • 클래스는 하나의 추상 클래스만 상속할 수 있습니다.
  • 예시
class MyClass : MyInterface {
    override fun doSomething() {
        // 구현
    }

    override val property: Int
        get() = 42
}

abstract class MyDerivedClass : MyAbstractClass() {
    // 추상 메서드 구현
    override fun doSomething() {
        // 구현
    }
}

 

간단하게 말하면,
인터페이스는 다중 상속 및 구현이 없는 메서드를 허용하며,
추상 클래스는 하위 클래스에서 반드시 구현해야 하는 메서드를 가지고 일반적으로 상속할 수 있는 클래스입니다.

 

계산기 만들기 (Lv4)

1) 기존에 만들던 Lv3 계산기에서 부터 시작했다. 먼저 abstract 추상클래스를 만들었고,

 

2)  AddOperation , MinusOperation, MultiplyOperation, DivideOperation, RestOperation
각각의 클래스에 :AbstractOperation 를 넣어 상속 시키고
각각의 fun operate 메소드에 override 시킨다.

 

3) class Calculator 에서 해당 객체가 가진 operation 을 가지고 연산하라는 메소드를 만들었다.

class Calculator (val operation : AbstractOperation) {
   fun operate(num1: Double, num2: Double) :Double {
       return operation.operate(num1, num2)
   }
}

 

4) 연산자 별로 각각의 객체를 주입했다.

val calcadd = Calculator(AddOperation())
val calcminus = Calculator(MinusOperation())
val calcmultiply = Calculator(MultiplyOperation())
val calcdivide = Calculator(DivideOperation())
val calcrest = Calculator(RestOperation())

 

5)  결과값에 해당 객체를 대입했다.

when (operator){
    "+" -> result = calcadd.operate(num1, num2)
    "-" -> result = calcminus.operate(num1, num2)
    "*" -> result = calcmultiply.operate(num1, num2)
    "/" -> result = calcdivide.operate(num1, num2)
    "%" -> result = calcrest.operate(num1, num2)

 

 

 

계산기 만들기 결과물

https://github.com/jeongminhan23/calculator

 

GitHub - jeongminy/calculator

Contribute to jeongminy/calculator development by creating an account on GitHub.

github.com

fun main() {
    val calcadd = Calculator(AddOperation()) // 더하기 연산을 하는 Calculator인스턴스 생성
    val calcminus = Calculator(MinusOperation()) // 빼기 연산을 하는 Calculator인스턴스 생성
    val calcmultiply = Calculator(MultiplyOperation()) // 곱하기 연산을 하는 Calculator인스턴스 생성
    val calcdivide = Calculator(DivideOperation()) // 나누기 연산을 하는 Calculator인스턴스 생성
    val calcrest = Calculator(RestOperation()) // 나머지 연산을 하는 Calculator인스턴스 생성

    // Calculator() 는 다른 종류의 연산을 수행할 수 있도록
    // 다양한 AbstractOperation의 구현체 AddOperation(), MinusOperation(), MultiplyOperation(), DivideOperation(), RestOperation() 를 받아들인다.
    // 이것이 가능한 이유는 Calculator 클래스의 생성자 매개변수로 AbstractOperation을 받기 때문이다.


    println("계산기를 실행합니다.")

    println("첫번째 숫자를 입력해 주세요")
    var num1 = readLine()!!.toDouble() // 사용자로부터 입력받은 문자열을 실수(Double)로 변환하는 코드
    // readLine() 함수는 사용자로부터 콘솔에서 한 줄을 읽어오는 함수이다. 이 함수의 반환 값은 사용자가 입력한 문자열이다.
    // !!(단언연산자)를 사용한 이유 : 이 연산자는 변수 또는 표현식이 null이 아님을 개발자가 명시적으로 단언할 때 사용한다.
    // readLine() 함수는 콘솔에서 사용자 입력을 받아오는 함수이며, 사용자가 아무런 입력을 하지 않거나 입력이 null인 경우가 있는데,
    // 이때, 사용자의 입력이 null이 아님을 확실히 알고 있을 때 !! 연산자를 사용하여 Kotlin 컴파일러에게 "이 변수는 null이 아님을 확실히 알고 있으니까 안전하게 사용해도 된다"고 알려주는 것이다.

    println("두번째 숫자를 입력해 주세요")
    var num2 = readLine()!!.toDouble()

    println("연산자를 입력해 주세요.")
    var operator = readLine()!!.toString()

    var result = 0.0
    // 초기 값을 설정하기 위한 것이다.
    // 프로그램이 시작될 때 result 변수를 초기화하여 나중에 계산된 결과를 저장하는 변수이다.
    // 0.0으로 초기화 한 이유: result가 실수형(Double)으로 선언되었기 때문
    // 변수를 초기화 하는 이유: 변수를 사용하기 전에 적절한 값으로 설정하여 안전성을 확보하고, 프로그램의 예측 가능성을 높이기 위함이다.


// 입력된 연산자에 따라 적절한 Calculator 인스턴스의 operate 메서드 호출
    when (operator) {
        "+" -> result = calcadd.operate(num1, num2) // 결과 = calcadd의 operate메소드(num1과 num2를 넣은) 이다.
        "-" -> result = calcminus.operate(num1, num2) // 결과 = calcminus 의 operate메소드(num1과 num2를 넣은) 이다.
        "*" -> result = calcmultiply.operate(num1, num2) // 결과 = calcmultiply 의 operate메소드(num1과 num2를 넣은) 이다.
        "/" -> result = calcdivide.operate(num1, num2) // 결과 = calcdivide 의 operate메소드(num1과 num2를 넣은) 이다.
        "%" -> result = calcrest.operate(num1, num2) // 결과 = calcrest 의 operate메소드(num1과 num2를 넣은) 이다.
    }

    println("${num1}과 ${num2}의 ${operator} 결과는 ${result} 입니다.") //계산기의 계산 결과를 콘솔에 출력한다.
}


class Calculator(val operation: AbstractOperation) {
    // Calculator 클래스는 AbstractOperation을 받아들이는 생성자를 가지고 있음.
    // 변수operation은 추상클래스AbstractOperation가 '타입' 으로 선언되었다.
    // 이로서 변수operation은 추상클래스AbstractOperation를 통해서
    // 추상클래스 AbstractOperation에게서 상속받은 자식클래스들(AddOperation, MinusOperation, MultiplyOperation, DivideOperation, RestOperation)을 다룰수 있게 되었다.
    // 이것은 객체지향 프로그래밍의 다형성에 해당한다.
    fun operate(num1: Double, num2: Double): Double { // num1과 num2는 이 메소드를 호출할 때 전달되는 값이다.
        return operation.operate(num1, num2) // operation객체 의 operate메서드를 호출하는 것으로, 실제로 어떤 연산을 수행할지는 operation 객체에 의해 결정
    } //
    //
}

// AbstractOperation은 추상 클래스로, 다양한 연산을 나타내는 클래스들이 이를 구현합니다.
abstract class AbstractOperation {
    abstract fun operate(num1: Double, num2: Double): Double // 하위위래스에서 부모크래스의 메소드를 사용하려면 반드시 메소드 앞에도 abstract를 붙여주어야 한다.
}

// 다양한 연산을 나타내는 AbstractOperation의 하위 클래스들
class AddOperation : AbstractOperation() { // AddOperation클래스를 정의함 // 추상클래스AbstractOperation 를 상속받고있음.
    override fun operate(num1: Double, num2: Double): Double = (num1 + num2).toDouble() //상속받은 operate 메소드를 오버라이딩하면서 자식 클래스의 기능을 추가하고 있음. // 여기서는 덧셈기능을 추가함
}

class MinusOperation : AbstractOperation() { // MinusOperation클래스를 정의함 // 추상클래스AbstractOperation 를 상속받고있음.
    override fun operate(num1: Double, num2: Double): Double = (num1 - num2).toDouble() //상속받은 operate 메소드를 오버라이딩하면서 자식클래스의 기능을 추가하고 있음. // 여기서는 뺄셈기능을 추가함
}

class MultiplyOperation : AbstractOperation() { // MultiplyOperation클래스를 정의함 // 추상클래스AbstractOperation 를 상속받고있음.
    override fun operate(num1: Double, num2: Double): Double = (num1 * num2).toDouble() //상속받은 operate 메소드를 오버라이딩하면서 자식클래스의 기능을 추가하고 있음. // 여기서는 뺄셈기능을 추가함
}

class DivideOperation : AbstractOperation() { // DivideOperation클래스를 정의함 // 추상클래스AbstractOperation 를 상속받고있음.
    override fun operate(num1: Double, num2: Double): Double {   //상속받은 operate 메소드를 오버라이딩하면서 자식클래스의 기능을 추가하고 있음. // 여기서는 나눗셈기능을 추가함
        // 0으로 나누는 경우 예외 처리를 추가하여 프로그램의 안정성을 높인다.
        // num2가 0이라면 "Divide by Zero"를 발생시켜줘서 예외를 처리해준다
        require(num2 != 0.0) {
            ArithmeticException("Divide by Zero")
        }
        return (num1 / num2).toDouble()
    }
}

class RestOperation : AbstractOperation() { // RestOperation클래스를 정의함 // 추상클래스AbstractOperation 를 상속받고있음.
    override fun operate(num1: Double, num2: Double): Double = (num1 % num2).toDouble() //상속받은 operate 메소드를 오버라이딩하면서 자식클래스의 기능을 추가하고 있음. // 여기서는 나머지기능을 추가함
}

 



블로그의 정보

꿈틀꿈틀 개발일기

jeongminy

활동하기