본문 바로가기

[TIL][내일배움캠프]

[내일배움캠프][TIL] 23.11.28 (화) - 2016년, 객체지향 프로그래밍 기초

1. 코드카타

 

오늘 푼 문제는 '2016년'이다.

 

 2016년
 2016년 1월 1일은 금요일이다. 이 때, a월 b일은 무슨 요일인지 return하는 solution 함수를 작성하기.
 ※ 2016년은 윤년이다.

 

역시나 String 문제 중 하나이다.

그렇지만 여느 문자열 문제와는 달리 수학적으로 접근할 수 있는 문제라서 나름 수월하다.

 

 

이번 문제의 문제점은 딱 하나다.

 

어떻게 깔끔하게 풀 수 있는가?

 

사실, 이 문제를 푸는 방법 자체는 매우 간단하다.

다만, 굉장히 노가다가 가미되고, 깔끔하지가 않아서 그렇게 썩 내키지 않아서 그렇지.

 

참고로, 그게 뭐냐면.

 

class Solution {    
    fun solution(a: Int, b: Int): String {
        var answer = ""
        var date: Int = b
        
        if (a == 1) date += 0
        else if (a == 2) date += 31
        else if (a == 3) date += 60
        else if (a == 4) date += 91
        else if (a == 5) date += 121
        else if (a == 6) date += 152
        else if (a == 7) date += 182
        else if (a == 8) date += 213
        else if (a == 9) date += 244
        else if (a == 10) date += 274
        else if (a == 11) date += 305
        else {
             date += 335
        }
        
        when(date%7){
            0 -> answer = "THU"
            1 -> answer = "FRI"
            2 -> answer = "SAT"
            3 -> answer = "SUN"
            4 -> answer = "MON"
            5 -> answer = "TUE"
            else -> {answer = "WED"}
        }
        
        return answer
    }
}

 

그냥 월 별로 일수를 다 더하고, 남은 일수를 더한 값을 7로 나눈 나머지로 요일을 정하는 것.

(1월 1일이 금요일이니 나머지가 1이면 금요일)

 

말했다시피 오류 자체는 없지만 굉장히 길기도 하거니와 구조적으로도 깔끔하지가 못하다.

압축해서 표현할 수도 있는 것을 굳이 풀어 쓴 느낌?

 

그래서 이 방법 외에 어떤 방법이 더 깔끔한지 찾아보기로 했다.

(참고로 제출은 위에 걸로 했다. 반면교사로다가)

 

 

그 결과 알아낸 것이 바로 배열이다.

class Solution {
    fun solution(a: Int, b: Int): String {        
        val month = listOf(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
        val Week = listOf("THU", "FRI", "SAT", "SUN", "MON", "TUE", "WED")
        var date = 0

        for (i in 0 until a - 1) {
            date += month[i]
        }

        date += b

        return Week[date%7]
    }
}

 

보아라.

원래라면 위에 식처럼 일일이 더해야하는 것을 이렇게 깔끔하게 날짜도 계산할 수 있고, 또 요일을 찾는 것도 저렇게 간편하게 표현할 수 있다.

 

한 눈에 봐도 깔끔한 코드이다.

이것이야말로 코딩의 진정한 묘미가 아닐까?

 

 

2. 객체지향 프로그래밍 기초

 

말은 이렇게 적어놨지만, 이것 또한 Kotlin 문법의 일부분이다.

단지 이전의 것이랑 구분하기 위해서 이렇게 적어놨을 뿐.

 

다만, 그 이전에 객체지향 프로그래밍이 무슨 말인지부터 알고 가자.

 

객체지향 프로그래밍 (Object-Oriented Programming, OOP)  : 소프트웨어 개발 방법론 중 하나로, 현실 세계의 사물과 개념을 프로그램 내애서 내에서 객체라는 단위로 모델링하여 프로그래밍하는 패러다임

 

…자세히 봐도 무슨 말인지 모르겠다. 넘어가자.

 

 

객체지향 프로그래밍의 특징

  • 클래스와 객체 (Class & Object)
  • 캡슐화 (Encapsulation)
  • 추상화 (Abstraction)
  • 상속 (Inheritance)
  • 다형성 (Polymorphism)

이제 Kotlin의 문법 중 하나인 객체지향 프로그래밍의 기초에 대해서 알아보도록 하자.

 

 

메소드 (Method)

 

특정한 로직을 가지는 소스코드에 별명(이름)을 붙인 것을 메소드라 하는데 쉽게 말해서 함수(Function)를 메소드라 생각하면 된다.

 

// 메소드의 기본 형태
fun 메소드이름(변수명:자료형, 변수명:자료형 ....) : 반환자료형 {
		소스코드 로직
}

 

 

클래스 (Class)

 

프로그램의 각 요소별 설계도라고 하며, 정보(property)와 행위(method)를 품고 있는 상위개체라고 하면 된다.

 

// 클래스의 기본 형태
class 클래스이름 {
	정보1
	정보2

	행위1
	행위2
}

 

 

○ 클래스의 특별한 형태

  • 데이터 클래스(Data Class) : 정보만 가지는 클래스
  • 실드 클래스(Sealed Class) : 소위 말하는 상속 클래스. 부모클래스와 자식클래스를 지정할 수 있다.
  • 오브젝트 클래스(Object Class) : Java의 static 대신 사용하는 키워드로, 프로그램을 실행하는 동시에 인스턴스화
  • 열거 클래스(Enum Class) : Comparable 인터페이스를 구현하는 추상 클래스

 

 

생성자

 

클래스를 실체화할 때 최초로 실행할 행위를 미리 세팅하는 것을 생성자라 한다.

 

○ 생성자의 종류

  • 기본 생성자
  • 명시적 생성자 : 주 생성자(Init)와 부 생성자(Constructor)로 나뉨

 

객체 (Object)

 

클래스 타입으로 선언된 것들을 지칭하며, 모든 인스턴스를 포함하는 개념이다.

(인스턴스 : 클래스 형태로 설계된 객체를 실체화한 것)

  • 정보와 행위를 작성한 클래스를 실체화해서 프로그램에 로딩 (메모리에 적재)
  • 정보와 행위가 그대로 로딩되는것이 아니라 위치정보를 메모리에 로딩
  • 프로그램은 객체의 위치정보를 변수에 저장해두고, 필요할 때 참조

 

상속

 

공통적인 요소들을 가지고 있는 클래스끼리 부모 / 자식 클래스로 구분해서 관계를 만드는 행위.

클래스(설계도) 간의 관계를 더 끈끈하게 만들어 다형성(polymorphism)을 구현하기 위함이며, 클래스의 내용을 변경해야 할 때 부모 클래스만 변경해도 되는 장점이 있다.

 

ex)

fun main() {
    var bird = Bird()
    var chicken = Chicken()
    var sparrow = Sparrow()
    var pigeon = Pigeon()

    bird.fly()
    chicken.fly()
    sparrow.fly()
    pigeon.fly()
}

open class Bird {
    fun fly() {
        println("새는 날아요~")
    }
}

class Chicken : Bird() {

}

class Sparrow : Bird() {

}

class Pigeon : Bird() {

}

여기서 Bird는 부모 클래스, Chicken, Sparrow, Pigeon은 자식 클래스가 되며, 자식 클래스들은 부모 클래스에 존재하는 fly() 함수를 상속받아서 따로 정의하지 않아도 사용할 수 있다.

 

 

오버라이딩 (Overriding)

 

상속받은 부모 클래스의 정보(property)나 행위(method)를 재설계하는 행위.

 

ex)

fun main() {
    var bird = Bird("새")
    var chicken = Chicken("닭", 2)
    var sparrow = Sparrow("참새", "갈색")
    var pigeon = Pigeon("비둘기", "서울")

    bird.fly()
    chicken.fly()
    sparrow.fly()
    pigeon.fly()
}

open class Bird(name:String) {
    var name: String = ""

    init {
        // this는 현재 클래스의 상태변수를 의미
        // var name: String = ""
        this.name = name
    }

    open fun fly() {
        println("${name}은 날아요~")
    }

}

class Chicken(name: String, age: Int) : Bird(name) {
    var age:Int = 0

    init {
        this.age = age
    }

    override fun fly() {
//        super객체는 부모의 객체를 의미하며 자동으로 생성됨
//        즉 부모객체의 fly메소드를 부르는 행위임
//        필요없으니 주석처리완료
//        super.fly()
        println("${age}살의 ${name}가 날아봅니다~ 꼬끼오!")
    }
}

class Sparrow(name: String, color: String) : Bird(name) {
    var color:String = ""

    init {
        this.color = color
    }

    override fun fly() {
//        super객체는 부모의 객체를 의미하며 자동으로 생성됨
//        즉 부모객체의 fly메소드를 부르는 행위임
//        필요없으니 주석처리완료
//        super.fly()
        println("${color}의 ${name}이 날아봅니다~ 짹짹!")
    }
}

class Pigeon(name: String, address: String) : Bird(name) {
    var address: String = ""

    init {
        this.address = address
    }

    override fun fly() {
//        super객체는 부모의 객체를 의미하며 자동으로 생성됨
//        즉 부모객체의 fly메소드를 부르는 행위임
//        필요없으니 주석처리완료
//        super.fly()
        println("${address} 살고있는 ${name}가 날아봅니다~ 구구!")
    }
}

이렇게 fly() 함수 앞에 open을 붙이면 자식 클래스에서 오버라이딩을 할 수 있으며, 출력하면 똑같은 메소드일지라도 각각 다른 문장이 출력된다.

 

 

오버로딩 (Overloading)

 

동일한 이름의 메소드를 여러 형태로 만드는 행위.

메소드의 개수, 혹은 자료형이 다르면 동일한 메소드를 사용할 수 있지만, 반환자료형(반환형)은 오버로딩에 영향을 주지 않는다.

 

ex)

fun main() {
    var calc = Calculator()
    
    var intResult = calc.add(1,2)
    var doubleResult = calc.add(1.2, 2.2)
    
    println("정수 덧셈결과: ${intResult}")
    println("실수 덧셈결과: ${doubleResult}")
    
}

class Calculator {
    
    fun add(num1: Int, num2: Int): Int {
        return num1+num2
    }
    
    fun add(num1: Double, num2: Double): Double {
        return num1+num2
    }
}

같은 메소드이지만 각각 다른 자료형(Int, Double)을 줬기 때문에 두 가지 모두 출력이 된다.