아이템 35
복잡한 객체를 생성하기 위한 DSL을 정의하라
DSL을 만들려면 리시버 개념을 이해해야 한다.
val myPlus: Int.(Int) -> Int = fun Int.(other:Int) = this + other
fun main() {
myPlus.invoke(1,2)
myPlus(1,2)
1.myPlus(2)
}
위 코드는 확장 함수를 나타내는 특별한 타입으로 리시버를 가진 함수 타입이다. 이처럼 리시버를 가진 함수 타입의 가장 중요한 특징은 this의 참조 대상을 변경할 수 있다는 것이다.
위 개념을 사용해 DSL을 구성해보면
fun createTable(): TableBuilder = table {
tr {
for (i in 1..2) {
td {
+"This is column$i"
}
}
}
}
fun table(init: TableBuilder.() -> Unit): TableBuilder {
....
}
class TableBuilder {
fun tr(init: TrBuilder.() -> Unit) {...}
}
class TrBuilder {
fun td(init: TdBuilder.() -> Unit) {...}
}
class TdBuilder {
var text = ""
operator fun String.unaryPlus() {
text += this
}
}
위 코드는 리시버를 가진 함수타입을 활용해 dsl을 작성했다. tr,td는 this키워드가 생략되어 각 스코프 별 Bulder를 가르킨다.
DSL을 사용할때 좋은 경우
- 복잡한 자료구조
- 계층적인 구조
- 거대한 양의 데이터
DSL 없이 빌더 또는 단순하게 생성자만 활용해도 원하는 모든 것을 표현할 수 있다.
DSL은 많이 사용되는 구조의 반복을 제거할 수 있게 해주고, 반복되는 코드를 간단하게 만들 수 있다.
하지만 사용자의 입장에서는 DSL이 내부적으로 어떻게 동작하는지 파악하기 어렵다. 이같은 단점을 고려해 꼭 필요한 경우에 DSL을 사용하자
'Reading Record > 이펙티브 코틀린' 카테고리의 다른 글
[이펙티브 코틀린] Item 37. 데이터 집합 표현에 data 한정자를 사용하라 (0) | 2022.06.07 |
---|---|
[이펙티브 코틀린] Item 36. 상속보다는 컴포지션을 사용하라 (0) | 2022.06.07 |
[이펙티브 코틀린] Item 34. 기본 생성자에 이름 있는 옵션 아규먼트를 사용하라 (0) | 2022.06.07 |
[이펙티브 코틀린] Item 33. 생성자 대신 팩토리 함수를 사용하라 (0) | 2022.06.07 |
[이펙티브 코틀린] Item 31 - 32. 문서 규약과 추상화 규약 (0) | 2022.05.29 |