なっく日報

技術やら生活やらのメモ

KotlinのDelegation(デリゲーション、委譲)とView adapterパターンの相性がよい件

最近少しずつ仕事でKotlinを触っているので、ちょっとしたメモ。

Delegation(デリゲーション、委譲)が便利

KotlinにはDelegationというデリゲーション、委譲(まんま)の実装を簡単にしてくれる機能があります。

https://kotlinlang.org/docs/reference/delegation.html

具体的にはJavaだと

class Base {
  private String name;

  Base(String name) {
    this.name = name;
  }

  public String getName() { 
    return this.name; 
  }
}
class Derived {
  private Base base;

  Derived(Base base) {
    this.base = base;
  }

  public String getName() { 
    return this.base.getName(); 
  }
  
  public void printName() {
    System.out.println(this.getName());
  }
}

// new Derived(new Base("名前")).printName();
// 名前

なコードは KotlinでInterfaceを準備すればスッキリ書けると。

interface Base {
    val name: String
}
class BaseImpl(override val name: String) : Base
class Derived(private val base: Base) : Base by base {
  fun printName() = println(this.name)
}

// Derived(BaseImpl("名前")).printName()
// 名前

(例がいまいちですが・・・)

View adapterとは

で、本題。

「レガシーソフトウェア改善ガイド」の「ビューレイヤーの複雑さ」という項目に出てきた「View adapter」なる概念があります。

DB → モデル → ビュー(と表示関連ロジック)

↑は↓にしなさいと。

DB → モデル → View adapter(と表示関連ロジック) → ビュー

テンプレートエンジンに書いてしまいそうな表示関連ロジックを、その言語で実装できるレイヤーに移して実装しましょうということだと理解しています。

JSPとかPHPで分岐がえらいことになっているレガシーなコードを見たことないでしょうか? FreeMarker/Thymeleaf/ERB/Pug どんな言語のどんなテンプレートエンジン使っていても起こりうる現象だと思います)

tech.kitchhike.com

この記事で紹介されているViewModelという概念にも近いと思います。

実装してみた

UserEntityなるモデルからfullNameを返す際にHTMLのタグで囲ってみました。

UserなるInterfaceにデフォルトプロパティ(Javaにはない)とデフォルトメソッドを実装する必要があります。

こんな感じ。UserViewがView adapterです。

interface User {
    val firstName: String;
    val lastName: String;
    val fullName get() = "${firstName} ${lastName}"
}

class UserEntity(
    override val firstName: String, 
    override val lastName: String
) : User

class UserView (
    val user: User
) : User by user { 
    override val firstName
        get() = "<span>${user.firstName}</span>"
    override val lastName
        get() = "<span>${user.firstName}</span>"
    override val fullName get() = "<div>${super.fullName} 様</div>"
}

fun main(args : Array<String>) { 
    val userEntity = UserEntity("太郎", "山田")
    val userView = UserView(userEntity)
    println(userEntity.fullName)
    println(userView.fullName)
}
// 山田 太郎
// <div><span>山田</span> <span>太郎</span> 様</div>

UserEntityをラップしたUserViewでHTMLのタグで囲ったfullNameを返却させることができました。

Interfaceが必要なのはちょっとまどろっこしいものの、なかなか良さ気に見えます。

実際現場で使っていくかは不明ですが、一つの手段として理解しておくと幸せになれることがあるかもしれません。

ほんでは。