본문 바로가기
JAVA & KOTLIN

[KOTLIN] Kotlin by 키워드: 상속보다 위임을 선택해야 하는 이유

by nozee 2024. 12. 16.
반응형

kotlin

Kotlin by 키워드: 상속보다 위임을 선택해야 하는 이유

Kotlin은 간결하고 효율적인 코드를 작성하도록 돕는 다양한 언어적 기능을 제공한다.

그중에서도 by 키워드는 위임(delegation)을 손쉽게 구현할 수 있게 해주는 도구로, 객체지향 프로그래밍과 합성(composition)의 이점을 극대화할 수 있도록 설계되었다.
이번 포스팅에서는 Kotlin의 by 키워드 사용법과 왜 상속보다 위임을 사용하는 것이 더 나은 선택인지를 중심으로 살펴본다.

 


 

1. 상속보다 위임을 선호해야 하는 이유

1.1 상속의 한계와 문제점

상속은 객체지향 프로그래밍에서 널리 사용되는 도구이다. 이를 통해 부모 클래스의 기능을 자식 클래스가 물려받아 재사용성을 극대화할 수 있다. 하지만 상속은 몇 가지 단점을 내포하고 있다.

  1. 강한 결합(Strong Coupling):
    자식 클래스는 부모 클래스의 구현 세부 사항에 강하게 결합된다. 이는 부모 클래스의 변경이 자식 클래스에 영향을 미치는 문제를 유발할 수 있다.
  2. 유연성 부족:
    Java와 Kotlin에서 클래스는 단일 상속만을 지원하므로, 다중 기능을 상속받아야 하는 경우 구조적으로 제약이 생길 수 있다.
  3. 클래스 계층 구조의 복잡성:
    상속을 무분별하게 사용하면 클래스 계층 구조가 불필요하게 복잡해져 유지보수가 어려워질 수 있다.

 

1.2 위임의 장점

위임은 상속과 달리 클래스 간의 느슨한 결합(loose coupling)을 유지하며 코드 재사용성을 높이는 데 도움을 준다. 상속 대신 위임을 사용하면 다음과 같은 이점이 있다.

  1. 유연한 설계:
    여러 객체의 기능을 위임받아 사용할 수 있으므로 다중 상속의 제약을 우회할 수 있다.
  2. 캡슐화 강화:
    내부적으로 다른 객체의 기능을 활용하되, 외부에서는 해당 객체의 세부 사항을 감출 수 있다.
  3. 독립적인 변경 가능:
    위임받은 객체의 변경이 위임하는 객체에 직접적인 영향을 주지 않으므로 유지보수가 용이하다.

 


 

2. Kotlin에서의 by 키워드 사용법

Kotlin은 위임 패턴을 간결하게 구현할 수 있도록 by 키워드를 제공한다. 이 키워드는 클래스나 인터페이스를 다른 객체에 위임할 때 사용된다. 이를 통해 중복 코드를 최소화하고 간단한 문법으로 위임을 구현할 수 있다.

2.1 인터페이스 위임

다음은 by 키워드를 사용하여 인터페이스 구현을 위임하는 간단한 예시이다.

 
interface Greeter {
    fun greet()
}

class SimpleGreeter : Greeter {
    override fun greet() {
        println("Hello from SimpleGreeter!")
    }
}

class DelegatingGreeter(private val greeter: Greeter) : Greeter by greeter
 

위 코드에서 DelegatingGreeter 클래스는 Greeter 인터페이스의 구현을 SimpleGreeter 객체에 위임한다.
이를 활용하면 다음과 같이 사용할 수 있다.

fun main() {
    val greeter = SimpleGreeter()
    val delegatingGreeter = DelegatingGreeter(greeter)
    delegatingGreeter.greet() // "Hello from SimpleGreeter!" 출력
}
 
 
 
아래는 같은 내용의 java인 코드
public interface Greeter {
    void greet();
}

public class SimpleGreeter implements Greeter {
    @Override
    public void greet() {
        System.out.println("Hello from SimpleGreeter!");
    }
}

public class DelegatingGreeter implements Greeter {
    private final Greeter greeter;

    public DelegatingGreeter(Greeter greeter) {
        this.greeter = greeter;
    }

    @Override
    public void greet() {
        greeter.greet();
    }
}

public class Main {
    public static void main(String[] args) {
        Greeter simpleGreeter = new SimpleGreeter();
        Greeter delegatingGreeter = new DelegatingGreeter(simpleGreeter);

        // Test the delegation
        delegatingGreeter.greet();
    }
}
 
코틀린이 더 간결하고 쉽게 사용하는 것을 볼 수 있다.
 

2.2 프로퍼티 위임

Kotlin은 프로퍼티의 Getter와 Setter를 다른 객체나 함수에 위임할 수 있도록 지원한다. 대표적으로 Lazy, Observable과 같은 기본 제공 위임자를 사용할 수 있다.

class Example {
    val lazyValue: String by lazy {
        println("Computed!")
        "Hello, Lazy!"
    }
}

fun main() {
    val example = Example()
    println(example.lazyValue) // "Computed!" 출력, "Hello, Lazy!" 출력
    println(example.lazyValue) // 캐싱된 값인 "Hello, Lazy!"만 출력
}

 

 


 

3. by를 활용한 위임의 실제 사례

3.1 로깅(logging) 위임

위임을 통해 특정 클래스가 로깅 기능을 별도의 로깅 클래스에 위임할 수 있다.

interface Logger {
    fun log(message: String)
}

class ConsoleLogger : Logger {
    override fun log(message: String) {
        println("Log: $message")
    }
}

class Service(private val logger: Logger) : Logger by logger {
    fun execute() {
        log("Service is executing.")
    }
}

fun main() {
    val logger = ConsoleLogger()
    val service = Service(logger)
    service.execute() // "Log: Service is executing." 출력
}

 

4. 결론

Kotlin의 by 키워드는 위임 패턴을 간결하게 구현할 수 있도록 설계된 강력한 도구이다. 이를 통해 상속의 단점을 극복하고, 보다 유연하고 확장 가능한 코드를 작성할 수 있다.

"상속보다는 위임을 선호하라"는 객체지향 설계의 원칙은 Kotlin의 철학과도 잘 맞아떨어진다. 위임은 객체 간의 결합도를 낮추고 코드의 재사용성을 높이는 동시에, 유지보수와 확장성을 개선할 수 있는 강력한 방법이다. Kotlin의 by 키워드를 활용하여 이러한 설계 원칙을 코드에 녹여보길 바란다.

 

Kotlin에서는 상속보다 위임을 사용하라는 의미에서 상속을 모두 여는 것이 아닌 open 키워드를 사용해서 상속을 사용할 class만 구분하도록 하였다.

 

긴 글 읽어 주셔서 감사합니다.
반응형