暇つぶしに書く。前回は例が実践的でなかったし、与太話も多かった。practical な情報を好む practical なプログラマには不評だったことだろう。
手も痛いので、さっくり終わらせる。
例えば、akka stream を利用した抽象的なインターフェースを書きたいとする。モチベーションは、実運用時の実装とテスト時等で実装を差し替えたいため、とかでいいだろう。実装を差し替えたい理由で一番多いのは、恐らくテストだ。
閑話休題。早速書いてみる。
trait XXXSources {
def allXXX: Source[XXX, NotUsed]
}
これは良くない。rigid だ。Materialized value が NotUsed で固定されてしまっている。実装によっては NotUsed では困るかもしれない。
そのため、普通はこうする。
trait XXXSources[Mat] {
def allXXX: Source[XXX, Mat]
}
これで良くなった。型パラメータは便利だ。
では次のような例ではどうか。所謂リポジトリパターンで、ソート済みのクエリ結果を返すメソッドシグネチャを考える。折角なので akka stream を例に用い続けよう。特に意味はない。
trait Repository[Entity, Mat] {
def sorted(): Source[Entity, Mat]
}
普通だ。この後、実装していくうちに、とある実装で昇順降順を指定できることが分かり、それに合わせてシグネチャを変更したくなったとする。
trait Repository[Entity, Mat] {
def sorted(ord: some.implement.Ord): Source[Entity, Mat]
}
特定の実装に依存したシグネチャはよくない。先の例のように型パラメタを使うことで解決できないか。
trait Repository[Entity, Ord, Mat] {
def sorted(ord: Ord): Source[Entity, Mat]
}
一見うまくみえるが、先ほどよりうまくない。使う側で、実装詳細の型に触れなくてはならない。
class RepositoryImpl[XXXEntity, some.implement.Ord, Future[Done]] { ... }
val repository = new RepositoryImpl
repository.sorted(some.implment.Ord.Asc) // oops
これでは抽象も何もあったものではない。
こういった時、つまり実装詳細の隠蔽をしたい時に抽象型メンバ(abstract type member)は輝く。
trait Repository[Entity, Mat] {
type Ord
def asc: Ord
def desc: Ord
def sorted(ord: Ord): Source[Entity, Mat]
}
こうなる。
class RepositoryImpl[Entity, Mat] {
type Ord = some.implement.Ord
def asc: Ord = some.implement.Ord.Asc
def desc: Ord = some.implement.Ord.Desc
def sorted(ord: Ord): Source[Entity, Mat] = ...
}
val repository = new RepositoryImpl
repository.sorted(repository.asc)
これで使う側は Ord がどのような型か知る必要はなくなった。どのような型か知らないため、生成することもできないために、メンバが増えてしまったが、これは仕方がない。一度隠した上で、見せたい部分だけ見せる、ということだ。
抽象型メンバ(abstract type member)は、実践的には Win32 API の HANDLE 型や、opaque pointer のような物だ。使う側には何の情報も与えず、実装を隠蔽する。これによりモジュール間を疎結合にしたり、実装の差し替えを容易にしたりする。
隠す必要がない時には型パラメタ、隠す必要があるときには抽象型メンバを、と覚えていれば大体それで良い。
ところで akka stream の Materialized value は、所謂実装詳細に属するものだ。一方で、利用者が Flow を制御するためのものという側面もある。結果として、抽象型メンバよりは、型パラメータにしておくのが望ましい。何事にも例外はある。
勿論抽象型メンバにしておくこともできるのだが、少々ややこしくなってしまう。どのようにややこしくなってしまうのか気になる人は、自分で書いてみよう。手が疲れただけといえば、その通りだ。
おしまい。