내배캠 클래스 세션

박미소·2023년 12월 25일
0

코틀린

목록 보기
14/44

상속(extend)과 추상화(abstract)



상속은 이미 존재하는 클래스의 기능을 확장하거나 수정하는 것에 초점을 맞춘다. 코드의 재사용성과 유지보수의 용이성을 제공한다.

추상화는 복잡한 시스템을 간단하게 표현하는 것에 초첨을 맞춘다.


  • 클래스와 추상 클래스의 차이점
클래스추상클래스
설계도미완성된 설계도
추상 메서드가 1개 이상 존재
인스턴스 생성상속을 통해 추상 메서드를 완성(구현)해야 인스턴스 생성 가능
꼭 필요한 메서드를 자손마다 다르게 구현될 것으로 예상되는 경우 사용



  • 상속 클래스 키오스크 예제

부모클래스로 여러가지 메뉴 객체 만들기

fun main(args: Array<String>) {
    val burgers = Menu("Burgers", "앵거스 비프 버거")
    val frozenCustard = Menu("Frozen Custard", "매장에서 신선하게 만드는 아이스크림")

    burgers.displayInfo()            // 이름 : Burgers, 설명: [ 앵거스 비프 버거 ]
    frozenCustard.displayInfo()      // 이름 : Frozen Custard, 설명: [ 매장에서 신선하게 만드는 아이스크림 ]

}



open class Menu(
    private val name: String,
    private val description: String,
) {
    open fun displayInfo() {
        println("이름 : $name, 설명: [ $description ]")
    }
}



+ 가변 리스트에 담아 for문으로 메뉴 나오게 하기

fun main(args: Array<String>) {
    val burgers = Menu("Burgers", "앵거스 비프 버거")
    val frozenCustard = Menu("Frozen Custard", "매장에서 신선하게 만드는 아이스크림")

    val menus = mutableListOf<Menu>()
    menus.add(burgers)
    menus.add(frozenCustard)

    for (menu in menus) {
        menu.displayInfo()      
        // 이름 : Burgers, 설명: [ 앵거스 비프 버거 ]
        // 이름 : Frozen Custard, 설명: [ 매장에서 신선하게 만드는 아이스크림 ]
    }

}



open class Menu(
    private val name: String,
    private val description: String,
) {
    open fun displayInfo() {
        println("이름 : $name, 설명: [ $description ]")
    }
}



+ 데이터니까 불가면 리스트에 담기

Menu를 상속받은 burgers, frozenCustard 객체 생성

fun main(args: Array<String>) {
    val burgers = Menu("Burgers", "앵거스 비프 버거")
    val frozenCustard = Menu("Frozen Custard", "매장에서 신선하게 만드는 아이스크림")

    val menus = listOf<Menu>(burgers, frozenCustard)

    for (menu in menus) {
        menu.displayInfo()
    }

}



open class Menu(
    private val name: String,
    private val description: String,
) {
    open fun displayInfo() {
        println("이름 : $name, 설명: [ $description ]")
    }
}



+ Menu 클래스를 상속받은 Food 클래스 - displayInfo() 오버라이딩 -> 음식 가격 추가

fun main(args: Array<String>) {

    val menus = listOf(                                  // 빈 리스트를 선언 할 경우에만 val menus  = listOf<Menu>() => <> 자리에 타입 명시
        Menu("burgers", "앵거스 비프 버거"),          
        Menu("Frozen Custard", "매장에서 신선하게 만드는 아이스크림")
    )


    for (menu in menus) {
        menu.displayInfo()
        // 이름 : burgers, 설명: [ 앵거스 비프 버거 ]
		// 이름 : Frozen Custard, 설명: [ 매장에서 신선하게 만드는 아이스크림 ]
    }


    1)
    val food1 = Food("ShackBurger", "토마토, 양상추, 쉑소스가 토핑된 치즈버거", 6.9)
    val food2 = Food("SmokeShack","베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거", 8.9)
	val foods = listOf(food1, food2)
    
    
    2)
    val foods = listOf(
        Food("ShackBurger", "토마토, 양상추, 쉑소스가 토핑된 치즈버거", 6.9),
        Food("SmokeShack","베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거", 8.9)
    )
    
    1.2 공통 for)
    for (food in foods) {
        food.displayInfo()
		// 이름: ShackBurger, 설명: [ 토마토, 양상추, 쉑소스가 토핑된 치즈버거 ], 가격: 6.9
		// 이름: SmokeShack, 설명: [ 베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거 ], 가격: 8.9
    }   
}



open class Menu(
    private val name: String,
    private val description: String,
) {
    open fun displayInfo() {
        println("이름 : $name, 설명: [ $description ]")
    }
}



open class Food(
    private val name: String,
    private val description: String,
    private val price: Double,
): Menu(name, description) {
    override fun displayInfo() {
        println("이름: $name, 설명: [ $description ], 가격: $price")
    }
}



+ Food 클래스 객체를 매개변수로 받은 Order 클래스, food 매개변수로 Food 클래스의 displayInfo() 접근

fun main(args: Array<String>) {

    println("-------------------------------------Food Menu-------------------------------------------------")

    val menus = listOf(                                  // 빈 리스트를 선언 할 경우에만 val menus  = listOf<Menu>() => <> 자리에 타입 명시
        Menu("burgers", "앵거스 비프 버거"),
        Menu("Frozen Custard", "매장에서 신선하게 만드는 아이스크림")
    )

    for (menu in menus) {
        menu.displayInfo()
    }

    

    println("-------------------------------------Food Menu-------------------------------------------------")

    val foods = listOf(
        Food("ShackBurger", "토마토, 양상추, 쉑소스가 토핑된 치즈버거", 6.9),
        Food("SmokeShack","베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거", 8.9)
    )


    for (food in foods) {
        food.displayInfo()
    }


    println("--------------------------------------장바구니 목록-----------------------------------------------")

    val orders: MutableList<Order> = mutableListOf()    // val orders = mutableListOf<Order>() 와 같음
    orders.add(Order(foods[0]))
    orders.add(Order(foods[1]))


    for (order in orders) {
        order.food.displayInfo()
    }
}



class Order(val food: Food) {

}



open class Menu(
    private val name: String,
    private val description: String,
) {
    open fun displayInfo() {
        println("이름 : $name, 설명: [ $description ]")
    }
}



open class Food(
    private val name: String,
    private val description: String,
    private val price: Double,
) : Menu(name, description) {
    override fun displayInfo() {
        println("이름: $name, 설명: [ $description ], 가격: $price")
    }
}




  • 추상 클래스 예제
abstract class Shape {
    abstract val name: String
    abstract fun area(): Double
}

class Rectangle(width: Double, height: Double) : Shape() {
    override val name: String = "Rectangle"
    private val w: Double = width
    private val h: Double = height
    override fun area(): Double = w * h
}

class Circle(radius: Double) : Shape() {
    override val name: String = "Circle"
    private val r: Double = radius
    override fun area(): Double = 3.14 * r * r
}






인터페이스



두 대상(객체) 간의 '연결, 대화, 소통'을 돕는 '중간 역할'을 한다.

추상 메서드의 집합.

구현된 것이 전혀 없는 설계도. 껍데기(모든 멤버가 public)



Interface with Companiton Object



클래스 안에서 쓰는 companion object declaration과 다름이 없다.
interface Vehicle {
    fun getNumberOfWheels(): Int

    companion object {
        const val DOUBLE_TRACK_AMOUNT_OF_WHEELS = 3
        fun isDoubleTrack(vehicle: Vehicle) = vehicle.getNumberOfWheels() > DOUBLE_TRACK_AMOUNT_OF_WHEELS
    }
}

동행객체는 공통 상수를 제공한다. 이 상수들은 더블트랙 자동차의 최소 바퀴수를 의미한다. 동행객체 안에 또 헬퍼 메서드 isDoubleTrack()도 있다. 인터페이스 내 공통 상수들과 헬퍼 메소드는 동행객체와 함께 일반적으로 사용된다. 동행객체는 인터페이스 구현에 일절 영향을 주지 않는다.

Helper method : 반복적으로 호출이 필요한 경우 매번 새로 정의하지 않고 한 번 선언이 돼 불필요하게 반복되는 코드를 줄여주는 목적으로 사용된다.



예제1

interface Vehicle {
    fun getNumberOfWheels(): Int   // 메서드에 할당한 숫자를 리턴. 변수처럼 사용.

    companion object {
        const val DOUBLE_TRACK_AMOUNT_OF_WHEELS = 3
        fun isDoubleTrack(vehicle: Vehicle) = vehicle.getNumberOfWheels() > DOUBLE_TRACK_AMOUNT_OF_WHEELS
    }
}



class Car(val brand: String, val model: String, val age: Int) : Vehicle {

    companion object {
        const val ANTIQUE_CAR_MINIMAL_AGE = 30
    }
    override fun getNumberOfWheels() = 4
    fun isAntique() = age >= ANTIQUE_CAR_MINIMAL_AGE
}



class VehicleImplementedInCompanionObject {            // 클래스 내 동행객체로 자전거 바퀴 수 싱글톤 생성

    companion object Bicycle : Vehicle {
        override fun getNumberOfWheels(): Int {
            return 2
        }
    }
}



fun main(args: Array<String>) {

    fun implementing_Vehicle(){
        val car = Car("Ford", "Mustang", 35)
        println(car.getNumberOfWheels())               // 4       // Car 클래스에 설정한 상수 4.
        println(Vehicle.isDoubleTrack(car))           // true    // 헬퍼 메소드 isDoubleTrack()의 조건문 불리언 리턴. 동행 객체 내 상수로 설정한 더블 트랙 자동차의 바퀴 수 3개 보다 Car 클래스에서 할당한 바퀴수 4개가 큰게 맞아서 true 리턴. getNumberOfWheels(car) = car.getNumberOfWheels() > DOUBLE_TRACK_AMOUNT_OF_WHEELS
        println(car.isAntique())                     // true    // isAntique() 조건문 차의 수명이 30년이 넘어서 true 리턴.
    }

    fun companion_Object_Implementing_Vehicle(){
        val bicycle = VehicleImplementedInCompanionObject.Bicycle
        println(bicycle.getNumberOfWheels())          // 2        // 인터페이스 메서드 getNumberOfWheels()에 할당된 자전서 바퀴수 2.
        println(Vehicle.isDoubleTrack(bicycle))      // false    // 인터페이스 내 생성된 동행객체 메서드 isDoubleTrack(bicycle) = bicycle.getNumberOfWheels() > DOUBLE_TRACK_AMOUNT_OF_WHEELS 자전거 바퀴 2개가 더블 트랙 바퀴 수 3개보다 작으니까 false 리턴.
    }

    implementing_Vehicle()
    companion_Object_Implementing_Vehicle()
}

Car 클래스는 Vehicle 인터페이스를 구현하고 있다. Car 클래스 내에서 동행객체가 새로 생성됐는데 인터페이스에서 생성됐던 동행객체에 영향을 주지 않는다.

프린트 해본 결과, car객체, bicycle객체 모두 인터페이스 내 getNumberOfWheels() 메서드에 접근하는 것에 차이가 없었다.

하지만, Car class로 여러개의 인스턴스를 생성할 수 있지만 VehicleImplementedInCompanionObject.Bicycle은 언제나 같은 싱글톤 인스턴스만 참조한다.






예제2

interface PlayingCard {
    abstract fun getCardNumber(): String
    fun getCardKing(): Int

    companion object {
        const val DIAMOND: Int = 3
        const val HEART: Int = 2
        fun winner(playingcard : PlayingCard) = playingcard.getCardKing() == 1  // king 카드를 가지고 있으면 1 아니면 0
    }
}



class User1(val age: Int): PlayingCard {

        override fun getCardNumber() = "3, 4, King"
        override fun getCardKing() = 1
        fun ageLimit() = age > 20
}




class User2(val age: Int): PlayingCard {

        override fun getCardNumber() = "1, 10, Queen"
        override fun getCardKing() = 0
        fun ageLimit() = age > 20
}
    
    
    
fun main(args: Array<String>) {   

    fun userOne() {
        val user1 = User1(38)
        println(user1.ageLimit())             // true     // 성인이어서 true
        println(PlayingCard.HEART)            // 2
        println(PlayingCard.DIAMOND)          // 
        println(PlayingCard.winner(user1))    // true     // king card를 갖고 있어 1이므로 true 리턴.
    }

    fun userTwo() {
        val user2 = User2(17)
        println(user2.ageLimit())                 // false    // 미성년자이므로 false
    }


    userOne()
	userTwo()
       
}



인터페이스의 장점

선언과 구현이 분리되어 있다.

느슨히 결합되어 있어 유지보수 하기 편하다.




디버그 방법

중단점 찍어서 벌레 누르기


깃허브에서 코드 예시 찾기







참고:

https://www.linkedin.com/pulse/abstract-classes-interfaces-kotlin-complete-guide-sagar-malhotra

https://www.baeldung.com/kotlin/companion-objects-interfaces

0개의 댓글