코틀린의 by 키워드란?
이번 스터디에선 코틀린의 by 키워드에 대해 발표를 했었습니다.
저도 by 키워드를 이번에 처음 보게 되었는데,, 정말 많이 공부를 해야할 거 같다는 생각이 들었습니다,,
by 키워드란!?
해당 디자인 패턴 구현을 쉽게 할 수 있도록 도와주는 키워드입니다!
우선 by 키워드를 이해하기 위해선 Delegate Pattern에 대해 알아야 합니다!!
바로 알아보시죠!!
Delegate Pattern이란?
디자인 패턴 중 하나인 Delegate Pattern은 어떠한 객체가 기능을 수행할 때
해당 기능을 다른 객체에게 위임하는 패턴입니다!
Delegate Pattern은 Composition(조합)을 이용하는 일반적인 패턴입니다.
그럼 여기서 잠깐!!!!!
"아니 굳이 조합(Delegate Pattern)을 사용할 필요가 있을까요?!"
"우리에겐 상속이라는 것이 있는데?????"
라는 생각이 드실 수도 있을 거 같아서 정리를 해봤습니다!
상속 대신 조합을 사용하는 이유!
상속을 사용했을 때 부모 클래스 구현에 있어서 어떠한 변화가 생기게 된다면 변화가 크던 작던 간에 자식 클래스에도
영향을 끼치게 됩니다. 이러한 이유 때문에 잘못된 상속은 객체의 유연성이 떨어지는 결과를 가져올 수 있습니다.
그럼 어떤 경우에 상속을 사용하여야 할까요?
이 때는 is-a / has - a의 차이를 생각해보면 됩니다
상속의 경우 확실한 상 하의 개념이 존재해야 하기 때문에 is - a가 성립됩니다
EX) 개는 동물이다 (Dog is a Animal)
조합의 경우 클래스 간의 어느 정도 종속성만 가지고 있을 때 사용됩니다
EX) 사람은 심장을 가지고 있다 (Person has a Heart)
Delegate Pattern을 사용한 예시!
대학생 H씨는 최근 자취방 계약이 끝나게 되어 새로운 자취방을 구하게 됩니다 하지만 부동산 계약은 너무나 어렵고 고려 해야 할 점이 많아 부동산 중개인 K씨를 만나 어느 정도 복잡한 부분을 맡기기로 결정을 했습니다
자, 이 글을 보시면 자취방을 구하는 대학생 H씨 즉 Student 클래스와
부동산 중인 K씨 즉 Broker 클래스가 존재할 수 있습니다.
Student 클래스는 부동산과 관련된 여러 권한들이 있을 것입니다.
(본인의 인증이 들어간 도장이나 서명 등)
부동산 중개인 K씨는 대학생 H씨의 권한을 위임 받아서 여러 부동산을 알아보고 계약을 진행할 것입니다.
이때 대학생 H씨의 대리인 역할을 하기 위해 도장이나 서명은 대학생 H씨의 것을
사용하도록 만들어봤습니다.
//Composition (조합) 사용
// 부동산과 관련된 권한들
interface Authority {
fun putStamp() // 도장 찍기
}
//학생 클래스! 결국 도장은 학생의 것
class Student(private val name: String) : Authority {
// Authority 인터페이스 구현
override fun putStamp() {
println("${name} 도장 쾅")
}
}
// 중개인 클래스 학생 대신 집을 알아봐준다
class Broker(private val student: Authority) : Authority {
// 도장은 학생의 권한을 위임받아 사용한다
override fun putStamp() {
student.putStamp()
}
}
fun main() {
//학생 H씨
val studentH = Student("학생 H씨")
//부동산중개인K씨
val brokerK = Broker(studentH)
brokerK.putStamp() // 학생에게 권한을 위임받아서 도장찍는다
}
자 위의 코드를 보시면 Authority 인터페이스에 도장 찍는 함수를 만들었고
그걸 Student 클래스와 Broker 클래스에서 상속 받았습니다
// 중개인 클래스 학생 대신 집을 알아봐준다
class Broker(private val student: Authority) : Authority {
// 도장은 학생의 권한을 위임받아 사용한다
override fun putStamp() {
student.putStamp()
}
}
이때 Broker 클래스는 Authority의 함수를 Student 클래스로 위임하는 모습을 볼 수 있습니다
override fun putStamp() {
student.putStamp()
}
또한 Broker의 override 함수를 보면 Student에서 선언한 함수를 사용하는 걸 보실 수 있습니다!
// Authority 인터페이스 구현
override fun putStamp() {
println("${name} 도장 쾅")
}
Student 클래스의 이 부분을 지정한다고 생각하시면 됩니다!
이후 출력을 해보면 이렇게 나옵니다
by 키워드를 사용하여 위의 예시를 바꿔보자!
위에 Composition에서 봤던 예제를 by 키워드를 사용하여 바꿔보겠습니다!!
//by 사용하기
// 부동산과 관련된 권한들
interface Authority {
fun putStamp() // 도장 찍기
}
//학생 클래스! 결국 도장은 학생의 것
class Student(private val name: String) : Authority {
// Authority 인터페이스 구현
override fun putStamp() {
println("${name} 도장 쾅 사용한다 by")
}
}
// 중개인 클래스 학생 대신 집을 알아봐준다
class Broker(private val student: Authority) : Authority by student {
// by 키워드를 통해 student 파라미터에게 권한을 위임
}
fun main() {
//학생 H씨
val studentH = Student("학생 H씨")
//부동산중개인K씨
val brokerK = Broker(studentH)
brokerK.putStamp() // 학생에게 권한을 위임받아서 도장찍는다
}
Broker 클래스가 매우 간결해졌습니다!
사실 Authority 인터페이스에 함수가 1개만 정의되어 있어서 엄청난 변화가 있는 건 아니지만
만약 인터페이스에 함수 여러 개가 정의 되어있을 경우 매우 유용할 것이라 생각이 듭니다.
간단하게 설명을 해보자면
// 중개인 클래스 학생 대신 집을 알아봐준다
class Broker(private val student: Authority) : Authority by student {
}
이 부분이 수정 되었는데
by 키워드를 사용하여 student 파라미터에 권한을 위임함으로써
굳이 override를 하지 않아도 composition을 사용했을 때와 동일한 값이 출력됩니다.
이후 출력을 하면 이렇게 나옵니다!
최종 정리!
위임 패턴은 상속을 대체할 수 있는 강력한 패턴입니다.
클래스의 위임을 통해 전면적인 코드 수정을 방지할 수 있다는 점에서 모듈을 보다 더 유연하게 구성할 수 있고
캡슐화와 다형성을 지킬 수 있습니다.