This commit is contained in:
usbharu 2025-01-27 12:06:39 +09:00
parent 2f97936c13
commit 7dfeb568d8
Signed by: usbharu
GPG Key ID: 8CB1087135660B8D
1 changed files with 84 additions and 0 deletions

View File

@ -0,0 +1,84 @@
---
author: usbharu
draft: false
categories:
- 技術
date: 2025-01-12T14:08:58+09:00
tags:
- Kotlin
- ActivityPub
- Kotlin
- ActivityStreams
- ActivityVocabulary
keywords:
- Kotlin
- ActivityPub
- Kotlin
- ActivityStreams
- ActivityVocabulary
title: Kotlinで作るDSL
relpermalink: posts/2025-01-12/
url: posts/2025-01-12/
decription: KotlinでデータとDSLが分離されたタイプのDSLの作成
---
KotlinでひたすらDSLを書いてたらだいぶウハウが溜まってきたので共有
KotlinのDSL作成でググるとこんな記事が出てくると思いますが、大半の人は下の記事を読むだけで作りたいものが作れると思うのでぜひ読んでください。
[Kotlin で書く DSL](https://improve-future.com/kotlin-dsl.html)
[Type-safe builders | Kotlin Documentation](https://kotlinlang.org/docs/type-safe-builders.html)
## データの実体とDSLが分離されたタイプのDSL
上で紹介した記事では全部データの実体となるクラスに直接DSLを生やしています。これはこれで綺麗にまとまっていいのかもしれませんが、僕は頭Javaな人なのでBuilderに相当する部分は分けられていてほしいです。特に内部でStringBuilderなどを使うようになってくるとややこしくなってきます。
分離すると何が嬉しいかというと
- DSL上の構造と実際のデータ構造が大幅に乖離しているときにDSLを書きやすい
- StringBuilderなどが使いやすい
- データ保持側に手を入れなくていい(責務の分離)
- クソややこしいDSLを書いたときにデータ保持側を読みやすい
- パフォーマンスが向上する場合がある(とりあえずListに溜め込んでbuild時に生成するStringBuilder的なアプローチがとれる)
逆にデメリットは
- 単純に考えて必要なクラス数が増える(どこまで凝るかによるが2倍以上)
- それに伴う保守性の低下
- Kotlinのうんこ仕様によるプロパティの露出
### メリット
#### DSL上の構造と実際のデータ構造が大幅に乖離しているときにDSLを書きやすい
実際のデータにDSLを生やした場合全然関係ないインスタンスを操作することは可能ですが不自然です。しかし、DSL部分を分離するとそのへんの制約は完全になくなって自由になります。
#### StringBuilderなどが使いやすい
上と同じで、データ側がStringBuilderをもともと持っているなら自然に使えますが、持ってない場合StringBuilderの意味がなくなります。
#### データ保持側に手を入れなくていい
そう、責務の分離ってやつです。わざわざ説明するまでもない
#### クソややこしいDSLを書いたときにデータ保持側を読みやすい
例えば[一つのオブジェクトのDSLに300行以上あったら](https://github.com/usbharu/activity-streams-serialization/blob/6e34c1ce3f2d070396c908f139b0863312262c45/src/main/kotlin/dev/usbharu/activitystreamsserialization/dsl/ObjectBuilder.kt)すごいややこしくないですか? そういうときに読みやすいです。
#### パフォーマンスが向上する場合がある
StringBuilderがいい例なんですが、一旦Listに溜め込んで、build時にインスタンスの生成するっていうアプローチを取れます。
これはKotlin標準のDSL
```kotlin
val buildString = buildString {
append("hello")
append(" ")
append("world")
for (i in 1..10) {
append(i)
}
}
```