KotlinでConstructor Injectionが増えてきたからDelegateをつかってリファクタリングしてみた
今回のエントリはDelegateの使い方をまとめる。
次のようなConstructor Injectionに複数のサービスクラスが並んだTaskBackendServerクラスがある。
@GRpcService class TaskBackendServer(private val getTaskService: GetTaskService, private val findTaskService: FindTaskService, private val createTaskService: CreateTaskService, private val updateTaskService: UpdateTaskService, private val deleteTaskService: DeleteTaskService, private val finishTaskService: FinishTaskService) : TaskServiceGrpc.TaskServiceImplBase() {
TaskBackendServerは複数のTaskServiceに依存していることが分かる。これくらいの数になると多くの依存が明確でり煩雑な印象を受ける。またテスト時にも依存クラスのインスタンスをつくりTaskBackendServerをつくりあげるのは骨が折れる。
そんなときには Delegate(委譲)
をつかって処理を委譲させるとよい。
DelegateをつかえばAクラスにBクラスのpublicな関数を委譲させることができる。委譲されたAクラスはBクラスの関数を使えるようになる。AクラスはCクラス、Dクラスなど委譲させる関数名にコンフリクトがなければ複数のクラスを委譲することができる。
上記のコードではTaskにまつわる複数のサービスクラスが TaskBackendServer
に依存しているので DelegateTaskService
クラスをつくり委譲をしてみる。
委譲させるクラスはインターフェースを実装したクラスであること
委譲のコードの前にGetTaskService
や FindTaskService
などそれぞれの委譲させるクラスは次のようにインターフェースを実装したクラスであることを整理しておく。
interface CreateTaskService { fun createTask(command: CreateTaskCommand): Task } @Service("createTaskService") class CreateTaskServiceImpl(private val taskRepository: TaskRepository) : CreateTaskService { @Transactional override fun createTask(command: CreateTaskCommand): Task { return taskRepository.create(command.title).fold({ task -> task }, { error -> throw handle(error) }) } }
DelegateTaskServiceをつくる
@Service class DelegateTaskService(private val getTaskService: GetTaskService, private val findTaskService: FindTaskService, private val createTaskService: CreateTaskService, private val updateTaskService: UpdateTaskService, private val deleteTaskService: DeleteTaskService, private val finishTaskService: FinishTaskService) : GetTaskService by getTaskService, FindTaskService by findTaskService, CreateTaskService by createTaskService, UpdateTaskService by updateTaskService, DeleteTaskService by deleteTaskService, FinishTaskService by finishTaskService
委譲させるにはクラス名の宣言のあとに、 by
キーワードをつかいDelegateを明示する。
TaskBackendServerのConstructor Injectionは次のようにスッキリした。
@GRpcService class TaskBackendServer(private val delegateTaskService: DelegateTaskService) : TaskServiceGrpc.TaskServiceImplBase() {
また、delegateTaskService
がGetTaskService
の関数を呼び出せるようになった。
val task = delegateTaskService.getTask(GetTaskCommand(taskId.toLong()))
まとめ
- Delegate(委譲)についてまとめた。
- 複数のインターフェースを実装する場合、委譲を使えばクラスの肥大化を抑えることができる。
- 今回のようにConstructor Injectionが増えてきて依存度が増しと感じれば委譲を使い整理することができる。
コード
githubにコードがありますので合わせて確認できます。
DelegateTaskService
はこちらです。 spring5-kotlin-application/DelegateTaskService.kt at master · soushin/spring5-kotlin-application · GitHub