[Kotlin] delegate 패턴이란? feat. 상속

박상군·2024년 11월 8일
0

Kotlin

목록 보기
9/9
post-thumbnail

내 코드도 클로드한테 위임해

코틀린에서 위임(delegate) 패턴상속(Inheritance)은 둘 다 객체의 재사용 및 확장을 위한 방법이지만, 사용하는 방식과 적용 목적에서 차이가 있다.

1. 상속(Inheritance)

부모 클래스의 기능을 자식 클래스가 상속받아 재사용할 수 있게 한다.
단일 상속만을 지원하므로, 하나의 클래스만 상속할 수 있다.
상속된 클래스는 부모 클래스의 메서드를 오버라이드할 수 있으며, 부모 클래스의 기능을 확장하거나 수정하여 사용할 수 있다.
상속 관계는 IS-A 관계라고 하며, 자식 클래스는 부모 클래스의 일종으로 간주된다.

Is-a 관계

  • Is-a는 추상화(형식이나 클래스와 같은)들 사이의 포함 관계를 의미하며, 한 클래스 A가 다른 클래스 B의 서브클래스(파생클래스)임을 이야기한다. 다른 말로, 타입 A는 타입 B의 명세(specification)를 암시한다는 점에서 타입 B의 서브타입이라고도 할 수 있다.

  • Is-a 관계는 타입 또는 클래스 간의 Has-a 관계와는 대조된다. Has-a 및 Is-a 관계들 간의 혼동은 실세계 관계 모델에 대한 설계에 있어 자주 발견되는 에러이다. Is-a 관계는 또한 객체 또는 타입 간의 instance-of 관계와도 대조된다.

특징

  • 부모 클래스의 메서드와 속성을 상속
  • 단일 상속만 가능
  • 부모 클래스와 강한 결합
  • 객체 간의 관계가 본질적으로 IS-A 관계일 때, 즉 한 객체가 다른 객체의 일종일 때 사용

사용 예시

open class Developer {
    open fun work() = "develop..."
}

class AndroidDeveloper : Developer() {
    override fun work() = "develop android app..."
}

class AppDeveloper: AndroidDeveloper()
val appDeveloper = AppDeveloper()
appDeveloper.work() // "develop android app..."

위와 같이 사용한다면 유연한 설계가 불가능하고 AppDeveloper는 AndroidDeveloper class에 강하게 결합된다.

2. 위임(Delegate) 패턴

특정 객체에 작업을 위임하여 해당 객체의 기능을 활용할 수 있다.
HAS-A 관계로, 객체는 위임된 클래스의 기능을 가지면서 해당 클래스와 협력하여 작업을 수행한다.
구성(composition)을 이용해 객체를 구성하고, 인터페이스나 다른 클래스의 구현을 위임함으로써 다중 상속이 필요한 경우 유용하게 사용할 수 있다.
코틀린에서는 by 키워드를 사용해 인터페이스에 대한 위임을 간단히 구현할 수 있다.

Has-a 관계

  • Has-a는 구성 관계를 의미하며 한 오브젝트(구성된 객체, 또는 부분/멤버 객체라고도 부릅니다)가 다른 오브젝트(composite type이라고 부릅니다)에 "속한다(belongs to)"를 말합니다. 단순히 말해, has-a 관계는 객체의 멤버 필드라고 불리는 객체를 말하며, Multiple has-a 관계는 소유 계층구조를 형성하기 위해 결합하는 경우를 말합니다.

특징

  • 위임 객체의 메서드를 사용
  • 여러 위임 객체를 통해 다중 기능 가능
  • 유연하며 교체 가능성 높음
  • 유연한 설계가 필요하거나, 다중 상속이 필요한 상황에서 구성(composition)을 통해 기능을 추가하고자 할 때 유용

사용 예시

interface Developer {
    fun work(): String
}

class AndroidDeveloper : Developer {
    override fun work() = "develop android app"
}

class AppDeveloper(private val developer: Developer) : Developer by developer
val appDeveloper = AppDeveloper(AndroidDeveloper())
appDeveloper.work() // "develop android app"

위와 같이 사용한다면 AppDeveloper는 AndroidDeveloper와 강하게 결합되지 않아 다음과 같이 유연한 사용이 가능하다.

class IOSDeveloper: Developer {
    override fun work(): String = "develop ios app"
}
val appDeveloper = AppDeveloper(IOSDeveloper())
appDeveloper.work() // "develop ios app"

위의 코드를 보면 AppDeveloper 클래스는 Developer 인터페이스를 구현하지만 AndroidDeveloper나 IOSDeveloper 클래스를 상속하지는 않는다.


정리

상속의 경우 is 관계가 확실하면 사용하는 것이 좋다.

  • 사람은 인간이다.
  • 강아지는 동물이다.

구성(composition)의 경우, 하나의 객체가 다른 객체에 부분적으로 포함 하는 경우 사용할 수 있다.

  • 스마트폰은 카메라를 가진다.
  • 자동차는 바퀴를 가진다.

References

Kotlin-Delegation
Is-a
Has-a

0개의 댓글