본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

[Effective Kotlin] Item 35 : Consider defining a DSL for complex object creation

by 돼지왕 왕돼지 2022. 5. 15.
반응형

이 글은 Effective Java 를 완독하고, Kotlin 을 상용으로 사용하는 개발자 입장에서
Effective Kotlin 글 중 새로운 내용, remind 할 필요 있는 부분, 핵심 내용 등만 추려 정리한 내용입니다.

 

#
HTML, View hierarchy, config 등등 많은 케이스에 적용될 수 있다.
돼왕 : 선언적인 느낌을 주기 좋은 것 같다.

 

#
만들기 복잡한 계층구조를 이루는 data 구조의 경우 DSL 로 하면 간단할 수 있다.
DSL 로 하면 Groovy 와 다르게 type safe 를 이룩할 수 있다.

 

 

Defining your own DSL

#
function type with receiver 의 개념을 이해해야 DSL 을 만들 수 있다.

 

#
function type 을 만드는 방법은 아래와 같다.

  1. lambda expression 
  2. val plus1: (Int, Int) -> Int = { a, b -> a + b }
  3. anonymous function.
     val plus2: (Int, Int) -> Int = fun(a, b) = a + b
  4. function reference.
     fun plus(a:Int, b: Int) = a + b
     val plus3 : (Int, Int) -> Int = ::plus

 

type inference 를 이용해서 아래와 같이 정의도 가능하다.

val plus4 = { a: Int, b : Int -> a + b }
val plus5 = fun(a: Int, b:Int) = a + b

 

#
function type with receiver 는 아래와 같이 정의된다.

fun Type.name(args){ /* ... */ }
val myPlus: Int.(Int) -> Int = fun Int.(other:Int) = this + other

이 경우 lambda 함수의 this 는 Type 객체가 된다. 이게 가장 큰 포인트이다.

 

#
function type with receiver 는 아래 3가지 형태로 invoke 될 수 있다.

  1. 일반 object 처럼 invoke 함수를 사용할 수 있음.
     myPlus.invoke(1, 2)
  2. non-extension function 처럼 사용할 수 있음.
     myPlus(1, 2)
  3. normal extension function 처럼 사용할 수 있음.
     1.myPlus(2)

 

#

fun createTable(): TableDsl = table{
	tr{
		for(i in 1..2){
			td{
				+"This is column $i"
			}
		}
	}
}

위의 DSL 을 만들기 위해서는 아래 함수정의들이 필요하다.

fun table(init: TableBuilder.()->Unit): TableBuilder{
	return TableBuilder().apply(init)
	// val tableBuilder  = TableBuilder()
	// init.invoke(tableBuilder)
	// return tableBuilder
}

class TableBuilder{
	var trs = listOf<TrBuilder>()
	fun tr(init: TrBuilder.() -> Unit) {
		trs = trs + TrBuilder().apply(init)
	}
}

class TrBuilder{
	var tds = listOf<TdBuilder>()

	fun td(init: TdBuilder.()->Unit) {
    	tds = tds + TdBuilder().apply(init)
	}
}

class TdBuilder {
	var text = ""
	operator fun String.unaryPlus() {
		text += this
	}
}

 

 

When should we use it?

#
DSL 은 정보를 정의하는 좋은 방법을 제공한다.
그러나 사용자 입장에서 DSL 에 전달된 정보가 어떻게 활용되는지는 알기 어렵고, 상황에 따라서 사용법이 복잡할 수 도 있다.
제작자 입장에서 코드 유지보수도 복잡하다. 퍼포먼스 이슈가 없는지 체크도 까다롭다.

더 쉬운 방법으로 대체할 수 있는 경우에 DSL 은 오버이다.
그러나 다음과 같은 경우에는 유용할 수 있다.

  1. 복잡한 데이터 구조
  2. 계층화된 구조
  3. 엄청난 양의 데이터

DSL 은 위와 같은 구조에서 발생하는 boilerplate 를 제거하기에 좋다.

 

 

Summary

 

 

 

반응형

댓글