ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TECHIT 앱 스쿨 2기: Android 16일차 (23.05.16)
    [THEC!T] 앱 스쿨2기 : Android 2023. 5. 17. 00:47
    728x90

    자료 출처 : 안드로이드 앱스쿨 2기 윤재성 강사님 수업 내용

     

    오늘은 어제 내주신 문제를 일부 수정하고 그에 대한 풀이 및 설명과 함께 시작하였습니다.

    어제 국민취업지원제도 상담일정이 겹쳐서 끝부분을 제대로 듣지 못했던 것 같습니다.

    문제는 오버라이딩의 개념을 이용하는 문제였으며, 문제에 대한 풀이와 설며으을 들으면서 오버라이딩에 대하여 다시 한번 설명해주셨습니다.

    오버라이딩 : 상속관계에 있는 자식 클래스에서 부모 클래스 내 메서드를 동일한 이름이지만 기능을 달리하게 만드는 것.

    코틀린에서의 상속관계는 :(콜론)을 이용하여 표시함. 자바 : extends

     

    그리고 문제에 대한 설명이 끝난 후에는 Any에 대해서 설명해주셨습니다. 

    간단하게 말하자면 개념적으로는 자바의 object와 동일한 개념입니다.

     

    Any

    코틀린에서 모든 클래스가 직접 혹은 간접적으로 상속받는 클래스 입니다.  따라서 모든 클래스는 Any를 상속받은

    상태이기 때문에 모든 객체들은 Any타입 변수에 담을 수 있습니다.

    클래스를 정의할 때 상속에 대한 코드를 작성하지 않았을 경우 Any 클래스를 상속 받습니다.

    Any 클래스 내부에는 클래스가 갖춰야할 기본적인 기능이 들어 있습니다.

     

    toString() : 객체의 정보 문자열을 반환하기 위해 만드는 메서드이며, println 과 같은 출력 문으로 출력하면

    toString 메서드를 호출하고 반환하는 문자열을 이용해 출력하게 됩니다.

     

    equals : == 연산자를 사용하면 자동으로 호출 되는 메서드 입니다. ==  연산자 기준으로 좌측의 잇는 변수를 통해

    메서드를 호출하고 우측에 있는 객체가 매개변수로 들어가며 해당 메서드가 반환하는 결과가 ==  연산의 결과가 됩니다.

     

    any클래스를 모든 클래스가 상속을 받은 상태이기 때문에 해당 연산자 또한 오버라이딩이 가능합니다.

    fun main (){
        // Any
        // 코틀린에서 모든 클래스가 직접 혹은 간접적으로 상속받는 클래스
        // 클래스를 정의할 때 상속에 대한 코드를 작성하지 않으면
        // Any 클래스를 상속받는다.
        // Any 안에는 클래스가 갖춰여야할 기본적인 기능이 들어 있다.
        // 모든 클래스는 Any를 상속 받은 상태이기 때문에
        // 모든 객체들은 Any타입 변수에 담을 수 있다.
    
        val a1:Any = TestClass1()
        val a2:Any = TestClass2()
    
        println(a1)
        println(a2)
    
        val t3 = TestClass3(100, 200)
        //val str1 = t3.toString()
        //println("str1 : $str1")
        println(t3)
    
        val t4 = TestClass3(1000,2000)
        val t5 = TestClass3(100, 200)
    
        if(t3 == t4){
            println("t3과 t4는 같습니다.")
        } else{
            println("t3과 t4는 다릅니다.")
        }
    
        if(t3 == t5){
            println("t3과 t5는 같습니다.")
        } else{
            println("t3과 t5는 다릅니다.")
        }
    
    }
    
    class TestClass1
    class TestClass2
    
    // Any 클래스가 제공하는 메서드 overriding
    class TestClass3(var a1:Int, var a2:Int){
        //toString
        // 객체의 정보 문자열을 반환하기 위해 만드는 메서드
        // println 과 같은 출력 문으로 출력하면
        // toString 메서드를 호출하고 이 메서드가 반환하는 문자열을
        // 이용해 출력하게 된다.
    //    override fun toString(): String {
    //        return "TestClass3을 통해 만든 객체"
    //    }
    
        // 강사님이 사용하는 방식
        // 객체의 변수값이나 필요한 정보를 구해 출력하는 용도로 사용한다.
        override fun toString(): String {
            println("a1 : $a1")
            println("a2 : $a2")
    
            return super.toString()
        }
    
        // == 연산자를 사용하면 자동으로 호출되는 메서드
        // ==  연산자 기준으로 좌측의 있는 변수를 통해 메서드를 호출하고
        // 우측에 있는 객체가 매개변수로 들어온다.
        // 이 메서드가 반환하는 결과가 == 연산의 결과가 된다.
        override fun equals(other: Any?): Boolean {
            if(other != null){
                // 형변환
                val temp = other as TestClass3
                if(this.a1 == temp.a1 && this.a2 == temp.a2){
                    return true
                } else {
                    return false
                }
            }
            return false
        }
    }
    
    
    class StudentClass(var name:String, var grade:Int = 0){
        override fun equals(other: Any?): Boolean {
            if(other !=null){
                val temp = other as StudentClass
                if(this.name == temp.name){
                    return true
                } else {
                    return false
                }
            }
            return false
        }
    }
    
    fun test(){
        val s1 = StudentClass("홍길동",1)
        val s2 = StudentClass("김길동",2)
        val s3 = StudentClass("홍길동",3)
    
        if(s1.name == s2.name){
            println("둘은 동명 2인 입니다.")
        }
        if(s1.name == s3.name){
            println("둘은 동명 2인 입니다.")
        }
    
    }

    출력 결과

    TestClass1@6acbcfc0
    TestClass2@5f184fc6
    a1 : 100
    a2 : 200
    TestClass3@6e8cf4c6
    t3과 t4는 다릅니다.
    t3과 t5는 같습니다.

     

    다음으로는 추상 클래스에 대해서 배웠습니다.

    추상 클래스 또한 자바와 개념 적으로는 큰 차이가 없으며 문법적으로 조금의 차이만 있을 뿐입니다.

     

    추상 클래스

    추상 클래스는 추상 메서드를 가지고 있는 클래스를 말합니다.

     

    추상 메서드

    추상 메서드는 구현되지 않은 메서드를 말합니다.

     

    추상 클래스는 완벽한 클래스가 아니기 때문에 객체를 생성할 수 없으며, 추상 클래스를 상속받는 클래스를 만든 뒤 추상 메서드를 오버라이딩하여 사용합니다.

     

    추상 클래스와 메서드는 abstract 키워드를 사용하며 상속이 가능하기에 open 키워드와 거의 세트로 같이 다닙니다.

    왜냐하면 open 을 하지 않으면 자바로 변환 될 때 final 클래스로 변환이 되어 상속이 불가능 하기 때문입니다.

    추상 클래스의 중요 목적 중 하나는 메서드 오버라이딩에 대한 강제성을 주기 위해 입니다.

    fun main(){
        // 추상 클래스
        // 추상 메서드를 가지고 있는 클래스
        // 추상 메서드
        // 구현되지 않은 메서드
        // 추상 클래스는 완벽한 클래스가 아니기 때문에 객체를 생성할 수 없다.
        // 추상 클래스를 상속받은 클래스를 만들고 추상 메서드를 오버라이딩하여
        // 사용한다.
        // 추상 클래스와 메서드는 abstract 키워드를 사용하며 상속이 가능해야 하기 때문에
        // 클래스에는 open 키워드를 사용하고 오버라이딩이 가능해야 하기 때문에
        // 추상 메서드에는 open 키워드를 사용해야 한다.
        // 추상 클래스는 메서드 오버라이딩에 대한 강제성을 주기 위해 사용한다.
    
        val t2 = TestClass2()
        t2.testMethod()
    
        val t3:TestClass1 = TestClass2()
        t3.testMethod()
    
    }
    
    // 추상 클래스 정의한다
    open abstract class TestClass1{
    
        //추상 메서드
        open abstract fun testMethod()
    }
    
    // 추상 클래스를 상속받은 클래스
    class TestClass2 : TestClass1(){
        override fun testMethod() {
            println("TestClass2의 testMethod")
        }
    
    }

    출력 결과

    TestClass2의 testMethod
    TestClass2의 testMethod

    그리고 추상 클래스에 대한 문제도 작성해 보았습니다.

     

    import java.util.*
    import kotlin.collections.ArrayList
    
    //중국집
    //
    //메뉴를 선택해주세요
    //1. 짜장면, 2. 짬뽕, 3. 볶음밥, 4. 종료
    //
    //주문 총 금액 : 30000원
    //짜장면 : 0개
    //짬뽕 : 0개
    //볶음밥 : 0개
    //
    //음식 : 짜장면
    //가격 : 6000원
    //곱배기여부 : 곱배기 입니다 or 곱배기 아닙니다
    //
    //음식 : 짬뽕
    //가격 : 8000원
    //홍합여부 : 홍합이 있습니다 or 홍합이 없습니다
    //
    //음식 : 볶음밥
    //가격 : 10000원
    //국물종류 : 짬뽕국물 or 계란국물
    //
    //
    //각 클래스를 통해 생성된 객체를 담을 ArrayList는 하나만 만들어준다.
    //
    //주문내역 출력시 주문한 음식 순서대로 출력한다.
    //
    //프로젝트의 이름은 Kotlin15_InheritEX1 로 해주세요
    
    fun main(){
        val chinessRestaurant = ChinessRestaurant()
        chinessRestaurant.selectMenu()
        chinessRestaurant.printMenu()
        chinessRestaurant.printFoodList()
    }
    
    class ChinessRestaurant{
    
        val scanner = Scanner(System.`in`)
        // 음식들을 담을 리스트
        val foodList = ArrayList<Food>()
    
        // 메뉴를 선택하는 기능
        fun selectMenu(){
            var selectNumber = 0
    
            while(true){
                println("메뉴를 선택해주세요")
                print("1. 짜장면, 2. 짬뽕, 3. 볶음밥, 4. 종료 : ")
                selectNumber = scanner.nextInt()
    
                if(selectNumber !in 1..4){
                    println("번호를 다시 입력해주세요")
                    continue
                }
                if(selectNumber == 4) {
                    break
                }
                when(selectNumber){
                    1 -> {
                        print("곱배기 인가요 ?(1. 곱배기O, 2. 곱배기X) : ")
                        val temp = scanner.nextInt()
                        var temp2 = true
                        if(temp == 2){
                            temp2 = false
                        }
                        val food = JJaJangMyun("짜장면", 6000, temp2)
                        foodList.add(food)
                    }
    
                    2 -> {
                        print("홍합이 있나요? (1. 있음, 2. 없음) : ")
                        val temp = scanner.nextInt()
                        var temp2 = true
                        if(temp == 2){
                            temp2 = false
                        }
                        val food = JJamPPong("짬뽕", 8000, temp2)
                        foodList.add(food)
                    }
                    3 -> {
                        print("국물 종류가 무엇인가요? *1. 짬뽕국물, 2. 계란국물 : ")
                        val temp = scanner.nextInt()
                        var temp2 = "짬뽕국물"
                        if(temp == 2){
                            temp2 = "계란국물"
                        }
                        val food = BockUmBab("볶음밥", 10000, temp2 )
                        foodList.add(food)
                    }
                }
            }
        }
        // 주문 내역을 출력하는 기능
        fun printMenu(){
            // 총 주문 금액
            var totalPrice = 0
            // 각 음식의 개수
            var jjaJangMyunCount = 0
            var jjamPPongCount = 0
            var bockUmBabCount = 0
            // 음식 금액 누적
            for(food in foodList){
                totalPrice += food.price
    
                when(food.name){
                    "짜장면" -> jjaJangMyunCount++
                    "짬뽕" -> jjamPPongCount++
                    "볶음밥" -> bockUmBabCount++
                }
            }
    
            println("주문 총 금액 : ${totalPrice}원")
            println("짜장면 : ${jjaJangMyunCount}개")
            println("짬뽕 : ${jjamPPongCount}개")
            println("볶음밥 : ${bockUmBabCount}개")
            println()
    
        }
        // 주문한 음식들을 출력하는 기능
        fun printFoodList(){
            // 짜장면
            for(food in foodList){
                food.printFoodInfo()
            }
    
        }
    }
    
    // 위에서 printFoodList 메서드에서 자식클래스에 오버라이딩한 printFoodInfo
    // 를 호출할 것이므로 메서드 오버라이딩에 대한 강제성을 주기위해
    // 추상클래스와 추상 메서드로 정의한다.
    open abstract class Food(var name: String, var price:Int){
        open abstract fun printFoodInfo()
    }
    
    
    class JJaJangMyun : Food{
    
        var isDouble = false
    
        constructor( name:String,  price:Int,  isDouble:Boolean) : super (name, price){
            this.isDouble = isDouble
        }
    
        override fun printFoodInfo() {
            println("음식 : $name")
            println("가격 : ${price}원")
    
            if (isDouble) {
                println("곱배기 여부 : 곱배기 입니다.")
            } else {
                println("곱배기 여부 : 곱배기 아닙니다.")
            }
    
        }
    }
    
    class JJamPPong : Food {
    
        var hasMuseel = false
    
        constructor( name:String,  price: Int,  hasMuseel:Boolean) : super(name, price){
            this.hasMuseel = hasMuseel
        }
    
        override fun printFoodInfo(){
            println("음식 : $name")
            println("가격 : ${price}원")
    
            if(hasMuseel){
                println("홍합 여부 : 홍합이 있습니다.")
            } else {
                println("홍합 여부 : 홍합이 없습니다.")
            }
    
        }
    }
    
    class BockUmBab : Food{
    
        lateinit var soupType : String
    
        constructor( name:String,  price:Int,  soupType:String) : super(name, price){
            this.soupType = soupType
        }
    
        override fun printFoodInfo(){
            println("음식 : $name")
            println("가격 : ${price}원")
            println("국물 종류 $soupType")
    
        }
    }

     

    이후에는 인터페이스에 대해서 학습하였습니다.

    인터페이스 또한 개념적으로는 자바와 동일합니다.

     

    인터페이스

    코틀린도 자바와 마찬가지로 다중 상속을 지원하지 않습니다. 따라서 자기 타입의 변수나 부모형 타입 변수에만 담을 수 있습니다. 하지만 생성된 객체의 주소 값을 다양한 타입에 변수에 담을 수 있도록 하고 싶을 때 사용하는 것이 인터페이스 입니다. 인터페이스는 클래스가 아니므로 객체를 생성할 때 사용하지는 못하지만 클래스는 한개 이상의 인터페이스를 구현할 수 있으며 생성된 객체는 구현한 인터페이스형 참조 변수에 담을 수 있습니다.

    인터페이스에는 추상 메서드와 이래반 메서드 모두를 구현해서 사용할 수 있으며, 추상 클래스와 목적이 비슷하지만 하나의 클래스에 여러 인터페이스를 구현할 수 있다는 장점이 있습니다.

     

    fun main(){
        val t1 = TestClass1()
        val t2 = TestClass2()
        val t3 = TestClass3()
        t1.abstractMethod1()
        t2.abstractMethod2()
        t3.abstractMethod3()
    
        var t4 = TestClass4()
        t4.interfaceMethod1()
        t4.interfaceMethod2()
        t4.interfaceMethod3()
    
        testFunction1(t1)
        testFunction2(t2)
    
        testFunction3(t4)
        testFunction4(t4)
    }
    
    fun testFunction1(t100: Abstract1){
        t100.abstractMethod1()
    }
    fun testFunction2(t200:Abstract2){
        t200.abstractMethod2()
    }
    
    fun testFunction3(t300 : Inter1){
        t300.interfaceMethod1()
    }
    fun testFunction4(t400 : Inter2){
        t400.interfaceMethod2()
    }
    
    
    // 추상 클래스
    open abstract class Abstract1{
        open abstract fun abstractMethod1()
    }
    
    open abstract class Abstract2{
        open abstract fun abstractMethod2()
    }
    
    open abstract class Abstract3{
        open abstract fun abstractMethod3()
    }
    
    // 추상 클래스 하나당 하나의 클래스를 만들어야 한다.
    class TestClass1: Abstract1(){
        override fun abstractMethod1() {
            println("TestClass1의 abstractMethod1")
        }
    }
    
    // 추상 클래스 하나당 하나의 클래스를 만들어야 한다.
    class TestClass2: Abstract2(){
        override fun abstractMethod2() {
            println("TestClass1의 abstractMethod1")
        }
    }
    
    // 추상 클래스 하나당 하나의 클래스를 만들어야 한다.
    class TestClass3: Abstract3(){
        override fun abstractMethod3() {
            println("TestClass1의 abstractMethod1")
        }
    }
    
    // 인터페이스
    interface Inter1{
        fun interfaceMethod1()
    }
    
    interface Inter2{
        fun interfaceMethod2()
    }
    
    interface Inter3{
        fun interfaceMethod3()
    }
    
    // 인터페이스를 정의한 클래스
    class TestClass4 : Inter1, Inter2, Inter3{
        override fun interfaceMethod1() {
            println("TestClass4의 interfaceMethod1")
        }
    
        override fun interfaceMethod2() {
            println("TestClass4의 interfaceMethod2")
        }
    
        override fun interfaceMethod3() {
            println("TestClass4의 interfaceMethod3")
        }
    }

     

    그리고 나서 인터페이스와 추상클래스 등을 최대한 사용하여 클래스 설계를 하는 문제를 내주셨습니다.

    저의 경우 해당 문제를 보고 나름 틀을 잡고 진행하였으나, 뭔가 중간중간 다른 방법이 더 좋을 거 같다는 생각이 들어 변경해 가면 작성하다 보니 시간이 훌쩍 지나가 완성하지 못하였었습니다.

     

    그리고 강사님께서 해당 문제를 통해 설계에 대한 방법에 대해서 알려주셨습니다.

    * 사람마다 다를 수 있습니다

     

    1. 어떠한 클래스들이 필요한지를 정리한다(한국어 주석)
    2. 각 클래스들의 이름을 정의한다(class 클래스명)
    3. 각 클래스가 가져야할 모든 변수와 모든 메서드들을 클래스 내부에 정의한다(한국어 주석)
    4. 한국어 주석으로 작성한 모든 변수와 모든 메서드들을 작성한다(멤버변수, 메서드는 { } 까지만)
    5. 각 클래스 별로 중복되는 요소들을 정리한다.
    6. 중복되는 요소들을 가지는 부모 클래스들을 정의한다.
    7. 정의된 부모 클래스들 중에 메서드로만 구성되는 것은 인터페이스로 변경한다.
    8. 부모 클래스들이 상속 받거나 구현해야 하는 인터페이스들을 설정해준다.
        8-1. 만약 자식 클래스가 구현해야 하는 메서드가 있다면 추상 메서드로 정의하고 클래스는 추상 클래스로 정의한다.
    9. 최종 자식 클래스에 상속과 인터페이스를 설정해준다.
    10. 최종 자식 클래스의 메서드들을 구현해준다.

     

    현업에서는 기획단계에서 많은 시간을 사용한다는 말씀과 같이 해주셨습니다. 강사님의 말씀을 듣고 다시금 틀을 확실히 잡은 뒤 코드를 작성해야 겠다는 생각을 하게 되었습니다.

     

    문제의 내용은 하기와 같습니다.

    강사님이 알려주신 코드를 보지 않고 강사님께서 알려주신 방법을 토대로 해보도록 하겠으나 다르거나 틀릴 수도 있습니다.

     

     

    문제

    //동물원
    //
    //동물사육장, 해양생물놀이터, 방목장, 밀림
    //
    //동물 사육장은 모든 동물들이 있는 곳
    //해양생물놀이터는 돌고래와 상어가 있는 곳
    //방목장은 말과 기린이 있는 곳
    //밀림은 호랑이와 사자가 있는 곳
    //
    //동물원에 있는 모든 동물들은 동물 사육장에 넣어주면 모든 동물들이 밥을 먹는다.
    //동물원에 있는 모든 동물들을 해양생물 놀이터에 넣어주면 돌고래와 상어가 헤엄을 친다.
    //동물원에 있는 모든 동물들을 방목장에 넣어주면 말과 기린과 호랑이만 뛰어 다닌다.
    //동물원에 있는 모든 몽물들을 밀림에 넣어주면 상어와 기린과 사자가 사냥을 한다.
    //
    //돌고래는 밥을 먹을 때 "냠냠냠"하고 먹는다.
    //상어는 밥을 먹을 때 "얌얌얌"하고 먹는다.
    //말은 밥을 먹을 때 "당근 당근"하고 먹는다.
    //기린은 밥을 먹을 때 "풀풀풀" 하고 먹는다.
    //호랑이가 밥을 먹을 때 "아작 아작"하고 먹는다.
    //사자가 법을 먹을 때 "꿀꺽 꿀꺽" 하고 는다.
    //
    //돌고래는 헤엄을 칠 때 "돌~돌~"하면서 헤엄을 친다.
    //상어가 헤엄을 칠 때 "슈웅 슈융" 하면서 헤엄을 친다.
    //말이 뛰어 다닐 때닌 "이히히히힝~" 하면서 뛰어 다니고
    //기린이 뛰어 다닐 때는 "영차~ 영차~" 하면서 뛰어 다닌다.
    //호랑이가 뛰어 다닐 때는 "헥~ 헥~" 하면서 뛰어 다닌다.
    //상어가 사냥을 하면 "으아아아아아!!!" 하면서 사냥을 한다.
    //기린이 사냥을 하면 "가즈아~" 하고 사냥을 한다.
    //사자가 사냥을 하면 "암컷아 사냥해와라~"하고 사냥을 한다.
    //
    //프로그램이 시작되면 다음과 같이 입력을 받는다.
    //어떤 동물을 동물원에 넣어줄까요?
    //1. 돌고래, 2. 상어, 3. 말, 4. 기린, 5. 호랑이, 6. 사자, 0. 그만넣어 :
    //
    //번호를 잘못 입력하면 잘못 입력하였습니다를 보여준다.
    //0을 입력하면 입력을 중단한다.
    //
    //모든 동물들을 사육장에 넣어 결과를 출력한다.
    //모든 동물들을 해양생물 놀이터에 넣어 결과를 출력한다.
    //모든 동물들을 방목장에 넣어 결과를 출력한다.
    //모든 동물들을 밀림에 넣어 결과를 출력한다.
    //
    //밥먹는 것, 헤엄치는 것, 뛰어 다는 것, 사냥하는 것은 모두 메서드로 만들어준다.
    //
    //--- 출력 ---
    //
    //타입 : 돌고래
    //크기 : 돌고래 만큼 크다
    //헤엄 속도 : 300노트
    //
    //타입 : 상어
    //크기 : 상어 만큼 크다
    //헤엄 속도 : 500노트
    //
    //타입 : 말
    //크기 : 말 만큼 크다
    //달리기 속도 : 300km/h
    //
    //타입 : 기린
    //크기 : 기린 만큼 크다
    //달리기 속도 : 500km/h
    //
    //타입 : 호랑이
    //크기 : 호랑이 만큼 크다
    //먹이량 : 500마리
    //
    //타입 : 사자
    //크기 : 사자만큼 크다
    //먹이량 : 600마리
    //
    //돌고래는 밥을 먹을 때 "냠냠냠"하고 먹는다.
    //상어는 밥을 먹을 때 "얌얌얌"하고 먹는다.
    //말은 밥을 먹을 때 "당근 당근"하고 먹는다.
    //기린은 밥을 먹을 때 "풀풀풀" 하고 먹는다.
    //호랑이가 밥을 먹을 때 "아작 아작"하고 먹는다.
    //사자가 법을 먹을 때 "꿀꺽 꿀꺽" 하고 는다.
    //
    //돌고래는 헤엄을 칠 때 "돌~돌~"하면서 헤엄을 친다.
    //상어가 헤엄을 칠 때 "슈웅 슈융" 하면서 헤엄을 친다.
    //말이 뛰어 다닐 때닌 "이히히히힝~" 하면서 뛰어 다니고
    //기린이 뛰어 다닐 때는 "영차~ 영차~" 하면서 뛰어 다닌다.
    //호랑이가 뛰어 다닐 때는 "헥~ 헥~" 하면서 뛰어 다닌다.
    //상어가 사냥을 하면 "으아아아아아!!!" 하면서 사냥을 한다.
    //기린이 사냥을 하면 "가즈아~" 하고 사냥을 한다.
    //사자가 사냥을 하면 "암컷아 사냥해와라~"하고 사냥을 한다.
    //
    //모든 출력은 항상 입력한 순서대로 출력한다.

     

    1. 어떠한 클래스들이 필요한지를 정리한다(한국어 주석)

    fun main(){
    
    }
    
    //1. 어떠한 클래스들이 필요한지를 정리한다(한국어 주석)
    
    // 동물원 클래스
    // 동물사육장 클래스
    // 해양생물놀이터 클래스
    // 방목장 클래스
    // 밀림 클래스
    // 돌고래 클래스
    // 상어클래스
    // 말 클래스
    // 기린 클래스
    // 호랑이 클래스
    // 사자 클래스

     

     

    2. 각 클래스들의 이름을 정의한다(class 클래스명)

    fun main(){
    
    }
    
    //2. 각 클래스들의 이름을 정의한다(class 클래스명)
    
    // 동물원 클래스
    class Zoo
    // 동물사육장 클래스
    class AnimalFarm
    // 해양생물놀이터 클래스
    class MarineLifePlayground
    // 방목장 클래스
    class Pasture
    // 밀림 클래스
    class Jungle
    // 돌고래 클래스
    class Dolphin
    // 상어클래스
    class Shark
    // 말 클래스
    class Horse
    // 기린 클래스
    class Giraffe
    // 호랑이 클래스
    class Tiger
    // 사자 클래스
    class Lion

     

    3. 각 클래스가 가져야할 모든 변수와 모든 메서드들을 클래스 내부에 정의한다(한국어 주석)

    fun main(){
    
    }
    
    //3. 각 클래스가 가져야할 모든 변수와 모든 메서드들을 클래스 내부에 정의한다(한국어 주석)
    
    // 동물원 클래스
    class Zoo{
        // 변수
        // 동물들을 저장할 리스트
    
        // 메서드
        // 추가할 동물들을 입력받는 메서드
        // 동물 사육장에 넣어주는 메서드
        // 해양 생물 놀이터에 넣어주는 메서드
        // 방목장에 넣어주는 메서드
        // 밀림에 넣어주는 메서드
    
    
    }
    // 동물사육장 클래스
    class AnimalFarm{
        // 메서드
        // 동물들이 밥을 먹는다
    }
    // 해양생물놀이터 클래스
    class MarineLifePlayground{
        // 메서드
        // 돌고래와 상어가 헤엄친다.
    }
    // 방목장 클래스
    class Pasture{
        // 메서드
        // 말과 기린 호랑이가 뛰어다닌다.
    }
    // 밀림 클래스
    class Jungle{
        // 메서드
        // 상어와 기린과 사자가 사냥을 한다.
    }
    // 돌고래 클래스
    class Dolphin{
        // 변수
        // 타입, 크기, 헤엄속도
    
        // 메서드
        // 자신의 정보를 출력한다.
        // 밥을 먹는다.
        // 헤엄을 친다.
    }
    // 상어클래스
    class Shark{
        // 변수
        // 타입, 크기, 헤엄속도
    
        // 메서드
        // 자신의 정보를 출력한다.
        // 밥을 먹는다.
        // 헤엄을 친다
        // 사냥을 한다.
    }
    // 말 클래스
    class Horse{
        // 변수
        // 타입, 크기, 달리기 속도
    
        // 메서드
        // 자신의 정보를 출력한다.
        // 밥을 먹는다
        // 뛰어 다닌다.
    }
    // 기린 클래스
    class Giraffe{
        // 변수
        // 타입, 크기, 달리기 속도
    
        // 메서드
        // 자신의 정보를 출력한다.
        // 밥을 먹는다.
        // 뛰어 다닌다.
        // 사냥을 한다.
    }
    // 호랑이 클래스
    class Tiger{
        // 변수
        // 타입, 크기, 먹이량
    
        // 메서드
        // 자신의 정보를 출력한다.
        // 밥을먹는다
        // 뛰어 다닌다.
    }
    // 사자 클래스
    class Lion{
        // 변수
        // 타입, 크기, 먹이량
    
        // 메서드
        // 자신의 정보를 출력한다.
        // 밥을 먹는다.
        // 사냥을 한다
    }

     

     

    4. 한국어 주석으로 작성한 모든 변수와 모든 메서드들을 작성한다(멤버변수, 메서드는 { } 까지만)

    fun main(){
    
    }
    
    //4. 한국어 주석으로 작성한 모든 변수와 모든 메서드들을 작성한다(멤버변수, 메서드는 { } 까지만)
    
    // 동물원 클래스
    class Zoo{
        // 변수
        // 동물들을 저장할 리스트
        val animalList = ArrayList<Animal>()
    
    
        // 메서드
        // 추가할 동물들을 입력받는 메서드
        fun addAnimal(){
    
        }
        // 동물 사육장에 넣어주는 메서드
        fun inAnimalFarm(){
    
        }
        // 해양 생물 놀이터에 넣어주는 메서드
        fun inMarineLifePlayground(){
    
        }
        // 방목장에 넣어주는 메서드
        fun inPasture(){
    
        }
        // 밀림에 넣어주는 메서드
        fun inJungle(){
    
        }
    
    
    }
    // 추가
    // 동물들을 모두 담을 수 있는 클래스 생성
    // 동물들을 저장할 리스트 선언하며 필요성 느껴 생성.
    class Animal{
    
    }
    
    
    // 동물사육장 클래스
    class AnimalFarm{
        // 메서드
        // 동물들이 밥을 먹는다
        fun animalEat(){
    
        }
    }
    // 해양생물놀이터 클래스
    class MarineLifePlayground{
        // 메서드
        // 돌고래와 상어가 헤엄친다.
        fun animalSwimming(){
    
        }
    }
    // 방목장 클래스
    class Pasture{
        // 메서드
        // 말과 기린 호랑이가 뛰어다닌다.
        fun animalRun(){
    
        }
    }
    // 밀림 클래스
    class Jungle{
        // 메서드
        // 상어와 기린과 사자가 사냥을 한다.
        fun animalHunt(){
    
        }
    }
    // 돌고래 클래스
    class Dolphin{
        // 변수
        // 타입, 크기, 헤엄속도
        var type = ""
        var size = ""
        var swimmingSpeed = 0
    
        // 메서드
        // 자신의 정보를 출력한다.
        fun printInfo(){
    
        }
        // 밥을 먹는다.
        fun eat(){
    
        }
        // 헤엄을 친다.
        fun swimming(){
    
        }
    }
    // 상어클래스
    class Shark{
        // 변수
        // 타입, 크기, 헤엄속도
        var type = ""
        var size = ""
        var swimmingSpeed = 0
    
        // 메서드
        // 자신의 정보를 출력한다.
        fun printInfo(){
    
        }
        // 밥을 먹는다.
        fun eat(){
    
        }
        // 헤엄을 친다.
        fun swimming(){
    
        }
        // 사냥을 한다.
        fun hunt(){
    
        }
    }
    // 말 클래스
    class Horse{
        // 변수
        // 타입, 크기, 달리기 속도
        var type = ""
        var size = ""
        var runSpeed = 0
    
        // 메서드
        // 자신의 정보를 출력한다.
        fun printInfo(){
    
        }
        // 밥을 먹는다.
        fun eat(){
    
        }
        // 뛰어 다닌다.
        fun hunt(){
    
        }
    }
    // 기린 클래스
    class Giraffe{
        // 변수
        // 타입, 크기, 달리기 속도
        var type = ""
        var size = ""
        var runSpeed = 0
        // 메서드
        // 자신의 정보를 출력한다.
        fun printInfo(){
    
        }
        // 밥을 먹는다.
        fun eat(){
    
        }
        // 뛰어 다닌다.
        fun run(){
    
        }
        // 사냥을 한다.
        fun hunt(){
    
        }
    }
    // 호랑이 클래스
    class Tiger{
        // 변수
        // 타입, 크기, 먹이량
        var type = ""
        var size = ""
        var feedingVolume = 0
    
        // 메서드
        // 자신의 정보를 출력한다.
        fun printInfo(){
            
        }
        // 밥을먹는다
        fun eat(){
            
        }
        // 뛰어 다닌다.
        fun run(){
            
        }
    }
    // 사자 클래스
    class Lion{
        // 변수
        // 타입, 크기, 먹이량
        var type = ""
        var size = ""
        var feedingVolume = 0
    
        // 메서드
        // 자신의 정보를 출력한다.
        fun printInfo(){
            
        }
        // 밥을 먹는다.
        fun eat(){
            
        }
        // 사냥을 한다
        fun hunt(){
            
        }
    }

    5. 각 클래스 별로 중복되는 요소들을 정리한다.

    //5. 각 클래스 별로 중복되는 요소들을 정리한다.
    
    // 5-1. 중복된 내용 : 각 동물들 클래스 내 변수(type,size), 메서드(printInfo())
    // 5-2. 중복된 내용 : 모든 동물들은 밥을 먹는다는 요소 
    // 5-3. 중복된 내용 : 돌고래와 상어는 헤엄을 친다는 요소
    // 5-4. 중복된 내용 : 말과 기린과 호랑이는 뛰어다닌다는 요소
    // 5-5. 중복된 내용 : 상어, 기린 사자가 사냥을 한다는 요소

    6. 중복되는 요소들을 가지는 부모 클래스들을 정의한다.

    4번에 작성한 코드들 중 일부 수정.

    open class Animal(var type:String, var size:String){
        open fun printInfo(){
    
        }
    }
    
    
    // 동물사육장 클래스
    open class AnimalFarm{
        // 메서드
        // 동물들이 밥을 먹는다
        open fun animalEat(){
    
        }
    }
    // 해양생물놀이터 클래스
    open class MarineLifePlayground{
        // 메서드
        // 돌고래와 상어가 헤엄친다.
        open fun animalSwimming(){
    
        }
    }
    // 방목장 클래스
    open class Pasture{
        // 메서드
        // 말과 기린 호랑이가 뛰어다닌다.
        open fun animalRun(){
    
        }
    }
    // 밀림 클래스
    open class Jungle{
        // 메서드
        // 상어와 기린과 사자가 사냥을 한다.
        open fun animalHunt(){
    
        }
    }

    7. 정의된 부모 클래스들 중에 메서드로만 구성되는 것은 인터페이스로 변경한다.

    6번에 작성한 클래스 중 메서드로만 구성되어 있는 클래스 인터페이스로 변경.

    // 동물사육장 인터페이스
    interface AnimalFarm{
        // 메서드
        // 동물들이 밥을 먹는다
        fun animalEat(){
    
        }
    }
    // 해양생물놀이터 인터페이스
    interface MarineLifePlayground{
        // 메서드
        // 돌고래와 상어가 헤엄친다.
        fun animalSwimming(){
    
        }
    }
    // 방목장 인터페이스
    interface Pasture{
        // 메서드
        // 말과 기린 호랑이가 뛰어다닌다.
        fun animalRun(){
    
        }
    }
    // 밀림 인터페이스
    interface Jungle{
        // 메서드
        // 상어와 기린과 사자가 사냥을 한다.
        fun animalHunt(){
    
        }
    }
    // 돌고래 인터페이스
    class Dolphin{
        // 변수
        // 타입, 크기, 헤엄속도
        var type = ""
        var size = ""
        var swimmingSpeed = 0
    
        // 메서드
        // 자신의 정보를 출력한다.
        fun printInfo(){
    
        }
        // 밥을 먹는다.
        fun eat(){
    
        }
        // 헤엄을 친다.
        fun swimming(){
    
        }
    }

    8. 부모 클래스들이 상속 받거나 구현해야 하는 인터페이스들을 설정해준다.
        8-1. 만약 자식 클래스가 구현해야 하는 메서드가 있다면 추상 메서드로 정의하고 클래스는 추상 클래스로 정의한다.

     

    해당 내용을 작성하다가 제가 처음 설계할 때 잊어버린 부분이 있었다는 것을 알게 되었습니다.

    그래서 그냥 적을까 말까 고민하다가 코드에는 정답이 없다고 말씀해주신 강사님 말씀이 떠올라 기능은 동일하게 작동하는 코드를 만들자 라는 생각이 들어 제가 작성한 코드를 바탕으로 제가 생각나는 대로 코드를 수정하여 작성해 보았습니다.

    사실 하기처럼 할꺼면 인터페이스도 굳이 쓸 필요가 없는 코드이긴 합니다. 하지만 저의 부족한 지식으로는 해당 방법 밖에 생각이 나지 않았습니다. 추후 다시 작성하여 코드를 올릴 수 있도록 하겠습니다.

     

    abstract open class Animal(var type:String, var size:String):AnimalFarm,MarineLifePlayground,Pasture,Jungle{
        abstract fun printInfo()
    }

     

    9. 최종 자식 클래스에 상속과 인터페이스를 설정해준다.

    // 돌고래 인터페이스
    class Dolphin(type: String, size: String): Animal(type, size){
        val swimmingSpeed = 300
        // 메서드
        // 자신의 정보를 출력한다.
        override fun printInfo(){
    
        }
        // 밥을 먹는다.
        override fun animalEat() {
    
        }
    
        override fun animalSwimming() {
    
        }
    
        override fun animalRun() {
    
        }
    
        override fun animalHunt() {
    
        }
    
    
    }
    // 상어클래스
    class Shark(type: String, size: String): Animal(type, size){
        // 변수
        // 타입, 크기, 헤엄속도
        val swimmingSpeed = 500
        override fun animalEat() {
    
        }
    
        override fun animalSwimming() {
    
        }
    
        override fun animalRun() {
    
        }
    
        override fun animalHunt() {
    
        }
    
        // 메서드
        // 자신의 정보를 출력한다.
        override fun printInfo(){
    
        }
    
    }
    // 말 클래스
    class Horse(type: String, size: String) : Animal(type, size) {
    
        val runSpeed = 300
        override fun printInfo() {
    
        }
    
        override fun animalEat() {
    
        }
    
        override fun animalSwimming() {
    
        }
    
        override fun animalRun() {
    
        }
    
        override fun animalHunt() {
    
        }
    
    
    }
    // 기린 클래스
    class Giraffe(type: String, size: String) : Animal(type, size) {
        val runSpeed = 500
        override fun printInfo() {
    
        }
    
        override fun animalEat() {
    
        }
    
        override fun animalSwimming() {
    
        }
    
        override fun animalRun() {
    
        }
    
        override fun animalHunt() {
    
        }
    
    
    
    }
    // 호랑이 클래스
    class Tiger(type: String, size: String) : Animal(type, size) {
    
        val feedingVolume = 500
        override fun printInfo() {
    
        }
    
        override fun animalEat() {
    
        }
    
        override fun animalSwimming() {
    
        }
    
        override fun animalRun() {
    
        }
    
        override fun animalHunt() {
    
        }
    
    
    }
    // 사자 클래스
    class Lion(type: String, size: String) : Animal(type, size) {
    
        val feedingVolume = 600
        override fun printInfo() {
    
        }
    
        override fun animalEat() {
    
        }
    
        override fun animalSwimming() {
    
        }
    
        override fun animalRun() {
    
        }
    
        override fun animalHunt() {
    
        }
    
    
    }

     

     

    10. 최종 자식 클래스의 메서드들을 구현해준다.

    해당 10번을 작성하면서 main() 과 Zoo클래스 내 메서드도 같이 작성하였습니다.

    import java.util.Scanner
    
    fun main(){
        val z1 = Zoo()
        z1.addAnimal()
        z1.printInfo()
        z1.inAnimalFarm()
        z1.inMarineLifePlayground()
        z1.inPasture()
        z1.inJungle()
    }
    //10. 최종 자식 클래스의 메서드들을 구현해준다.
    
    
    // 동물원 클래스
    class Zoo{
        // 변수
        // 동물들을 저장할 리스트
        val animalList = ArrayList<Animal>()
    
        // 사용자의 입력을 받을 스캐너
        val scanner = Scanner(System.`in`)
    
        // 메서드
        // 추가할 동물들을 입력받는 메서드
        fun addAnimal(){
            while(true){
                println("어떤 동물을 동물원에 넣어줄까요?")
                print("1. 돌고래, 2. 상어, 3. 말, 4. 기린, 5. 호랑이, 6. 사자, 0. 그만넣어 :")
                val typeNumber = scanner.nextInt()
                if(typeNumber !in 0 .. 6){
                    println("잘못 입력하셨습니다.")
                    continue
                }
                if(typeNumber == 0) break
                when(typeNumber){
                    1 -> animalList.add(Dolphin("돌고래","돌고래 만큼 크다"))
                    2 -> animalList.add(Shark("상어","상어 만큼 크다"))
                    3 -> animalList.add(Horse("말", "말 만큼 크다"))
                    4 -> animalList.add(Giraffe("기린","기린 만큼 크다"))
                    5 -> animalList.add(Tiger("호랑이", "호랑이 만큼 크다"))
                    6 -> animalList.add(Lion("사자", "사자만큼 크다"))
                    else -> animalList.add(Lion("123", "123"))
                }
            }
        }
        // 저장된 동물들의 정보를 출력하는 메서드
        fun printInfo(){
            for(animal in animalList){
                animal.printInfo()
                println()
            }
    
        }
        // 동물 사육장에 넣어주는 메서드
        fun inAnimalFarm(){
            for(animal in animalList){
                animal.animalEat()
            }
            println()
        }
        // 해양 생물 놀이터에 넣어주는 메서드
        fun inMarineLifePlayground(){
            for(animal in animalList){
                animal.animalSwimming()
            }
            println()
        }
        // 방목장에 넣어주는 메서드
        fun inPasture(){
            for(animal in animalList){
                animal.animalRun()
            }
            println()
        }
        // 밀림에 넣어주는 메서드
        fun inJungle(){
            for(animal in animalList){
                animal.animalHunt()
            }
            println()
        }
    
    
    }
    // 추가
    // 동물들을 모두 담을 수 있는 클래스 생성
    // 동물들을 저장할 리스트 선언하며 필요성 느껴 생성.
    // 동물 클래스 중 중복되는 내용 정리
    abstract open class Animal(var type:String, var size:String):AnimalFarm,MarineLifePlayground,Pasture,Jungle{
        abstract fun printInfo()
    }
    
    
    // 동물사육장 인터페이스
    interface AnimalFarm{
        // 메서드
        // 동물들이 밥을 먹는다
        fun animalEat()
    
    
    }
    // 해양생물놀이터 인터페이스
    interface MarineLifePlayground{
        // 메서드
        // 돌고래와 상어가 헤엄친다.
        fun animalSwimming()
    }
    // 방목장 인터페이스
    interface Pasture{
        // 메서드
        // 말과 기린 호랑이가 뛰어다닌다.
        fun animalRun()
    }
    // 밀림 인터페이스
    interface Jungle{
        // 메서드
        // 상어와 기린과 사자가 사냥을 한다.
        fun animalHunt()
    }
    // 돌고래 인터페이스
    class Dolphin(type: String, size: String): Animal(type, size){
        val swimmingSpeed = 300
        // 메서드
        // 자신의 정보를 출력한다.
        override fun printInfo(){
            println("타입 : $type")
            println("크기 : $size")
            println("헤엄 속도 : ${swimmingSpeed}노트")
        }
        // 밥을 먹는다.
        override fun animalEat() {
            println("${type}은 냠냠냠 하고 먹습니다.")
        }
    
        override fun animalSwimming() {
            println("${type}은 돌~돌~ 하면서 헤엄칩니다.")
        }
    
        override fun animalRun() {
            return
        }
    
        override fun animalHunt() {
            return
        }
    
    
    }
    // 상어클래스
    class Shark(type: String, size: String): Animal(type, size){
        // 변수
        // 타입, 크기, 헤엄속도
        val swimmingSpeed = 500
        override fun animalEat() {
            println("${type}은 얌얌얌하며 먹습니다.")
        }
    
        override fun animalSwimming() {
            println("${type}은 슈웅 슈웅하며 헤엄 칩니다.")
        }
    
        override fun animalRun() {
            return
        }
    
        override fun animalHunt() {
            println("${type}은 으아아아아아!!! 하면서 사냥합니다.")
        }
    
        // 메서드
        // 자신의 정보를 출력한다.
        override fun printInfo(){
            println("타입 :$type")
            println("크기 : $size")
            println("헤엄 속도 : ${swimmingSpeed}노트")
        }
    
    }
    // 말 클래스
    class Horse(type: String, size: String) : Animal(type, size) {
    
        val runSpeed = 300
        override fun printInfo() {
            println("타입 : $type")
            println("크기 : $size")
            println("달리기 속도 : ${runSpeed}km/h")
        }
    
        override fun animalEat() {
            println("${type}은 당근 당근하며 먹습니다.")
        }
    
        override fun animalSwimming() {
            return
        }
    
        override fun animalRun() {
            println("${type}은 이히히히힝~ 하며 달립니다.")
        }
    
        override fun animalHunt() {
            return
        }
    
    
    }
    // 기린 클래스
    class Giraffe(type: String, size: String) : Animal(type, size) {
        val runSpeed = 500
        override fun printInfo() {
            println("타입 : $type")
            println("크기 : $size")
            println("달리기 속도 : ${runSpeed}km/h")
        }
    
        override fun animalEat() {
            println("${type}은 풀풀풀 하며 먹습니다.")
        }
    
        override fun animalSwimming() {
            return
        }
    
        override fun animalRun() {
            println("${type}은 영차~ 영차~하며 뜁니다.")
        }
    
        override fun animalHunt() {
            println("${type}은 가즈아~하며 사냥합니다.")
        }
    
    
    
    }
    // 호랑이 클래스
    class Tiger(type: String, size: String) : Animal(type, size) {
    
        val feedingVolume = 500
        override fun printInfo() {
            println("타입 : $type")
            println("크기 : $size")
            println("먹이량 : ${feedingVolume}km/h")
        }
    
        override fun animalEat() {
            println("${type}은 아작 아작하며 먹습니다.")
        }
    
        override fun animalSwimming() {
            return
        }
    
        override fun animalRun() {
            println("${type}은 헥~ 헥~하며 뜁니다.")
        }
    
        override fun animalHunt() {
            return
        }
    
    
    }
    // 사자 클래스
    class Lion(type: String, size: String) : Animal(type, size) {
    
        val feedingVolume = 600
        override fun printInfo() {
            println("타입 : $type")
            println("크기 : $size")
            println("먹이량 : ${feedingVolume}km/h")
        }
    
        override fun animalEat() {
            println("${type}은 꿀꺽 꿀꺽하며 먹습니다.")
        }
    
        override fun animalSwimming() {
            return
        }
    
        override fun animalRun() {
            return
        }
    
        override fun animalHunt() {
            println("${type}은 암컷아 사냥해와라~하며 사냥합니다.")
        }
    
    
    }

     

     

    마무리

    오늘 또 다시금 설계의 중요성을 알게 되었습니다. 상속과 인터페이스 들을 왠만하면 사용하며 프로그램을 설계하려니 뭔가 어려웠습니다. 하지만 해당 부분을 잘하게 되면 길게 보았을 경우에는 회사에 들어가서 협업을 할 때 많은 도움이 될 것이며, 짧게는 안드로이드쪽을 배우게 되었을때 좀 더 보기 편한 방식으로 작성할 수 있지 않을까? 라는 생각과 코딩테스트를 연습할 때 좀 더 헷갈리지 않도록 작성할 수 있지 않을까? 라는 생각이 들었습니다.

    그리고 분명 글을 쓰기 시작할 때는 9시 쯤이였던 것 같은데..... 다 쓰고 보니 거의 1시가 되어 가네요.. 아직 익숙하지 않은 방식으로 코드를 작성하다 보니 더욱 오래 걸렸던 것 같습니다. 하지만 시간 단축은 반복이 답인 것을 알고 있기에 늦어도 이번주 내로 다시 설계하여 작성할 수 있도록 하겠습니다.

     

    오늘의 마음가짐

    실패는 경험이 되어 추후 실패할 확률을 낮춰주며, 실패하더라도 회복할 수 있는 방법을 알려주는 성공에 대한 방향표라고 생각하자~!

     

Designed by Tistory.