Swift で Rails の decorator みたいなのを作ってみる

  • 2
    Like
  • 0
    Comment

Classi Advent Calendar 2016 16 日目です!
iOS のアーキテクチャを悩んでいる時に、 Rails の decorator について教えてもらったので、それを Swift で実践してみます。

Rails の decorator って?

有名どころはここら辺。

View で表示するために Model のデータを加工したいとき、 Model にメソッドを生やす手段が一番早い。
でも、 View で使うだけのメソッドを Model に記述するのは責務的にどうなの?

という時に使うやつで、使い方は以下のように書きます。

# decorate してない場合は full_name にアクセスできない
@user = User.first
p @user.full_name
# => method_missing

# decorate したあとは full_name にアクセスできる
@user = User.first.decorate
p @user.full_name
# => "宮本 フレデリカ"

Model に extension 生やす (今までのやり方)

class User {
    let lastName: String
    let firstName: String

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
}

extension User {
    var fullName: String {
        return lastName + " " + firstName
    }
}

let user = User(firstName: "卯月", lastName: "島村")
print(user.fullName)
// => 島村 卯月

Model に fullName のプロパティを生やした。
fullName は画面に表示するためだけに使うので、 Model にあるのおかしいのでは?

Decoration してみる

// 普通に User クラスを作る
class User {
    let lastName: String
    let firstName: String

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
}

// draper の User.first.decorate のようにアクセス出来るようにする
extension User {
    var decorate: UserDecorator {
        return UserDecorator(user: self)
    }
}

// Decoration クラスを作成し、 fullName プロパティを生やす
class UserDecorator {
    private let user: User

    init(user: User) {
        self.user = user
    }

    var fullName: String {
        return user.lastName + " " + user.firstName
    }
}

使い方:

let user = User(firstName: "凛", lastName: "渋谷")
let decoratedUser = UserDecorator(user: user)
print(decoratedUser.fullName)
// => 渋谷 凛

let decoratedUser2 = User(firstName: "未央", lastName: "本田").decorate
print(decoratedUser2.fullName)
// => 本田 未央

これで ruby と同じようなことができるようになりました :tada:

感想

Model は綺麗になり、 View で使うやつだけ Decorator に定義すれば良くなりました :relaxed:
実際に使うかは検討中で、fullName くらいだったら Model が持っててもいいかなと思うけど、大きくデコる必要が出てきた時に使おうかなと思います。

また、Ruby は動的言語なのでコントローラ側で decorate しても、 View 側が decorate されてるかそうでないかを意識する必要があります。
Swift は静的言語なので明示的に UserDecorator 型で受ける必要があり、 fullName でアクセスできる型なのかが明示的で良いと思いました。

と言っても Rails の場合は動的型付けで同じ User と認識すれば良いのですが、Swift の場合全くの別クラスとなってしまうので Swift 的な流儀としてどうなのでしょうか。
それをするくらいなら extension 生やせば?とも思うので、状況によって使い分けしていきたいです。

おまけ: Decorator パターン

draper は Decorator パターン だという意見を見ましたが、新しいメソッドを生やすというのは Decorator パターンではないのでは?
Decorator パターンは継承をせずに再帰的に実行できるのが良いところで、新しいメソッドを生やすのは違うと思いました。

最後に

Classi 株式会社ではエンジニアを募集しています。
教育に興味ある人、ともに成長したい人来てくれ!!!頼む!!!

日本の教育を変える!先生・生徒向けアプリを作るiOSエンジニア募集! - Wantedly