Subscribed unsubscribe Subscribe Subscribe

@kyanny's blog

Write down what I learnt. Opinions are my own.

破れ紙の片割れのようなMixinができてしまうワケ

最近「Mixin は避けられれば避けたほうがよいのではないか」と思い始めている。扱いに困るダメ実装の Mixin を簡単に作れすぎてしまうからだ。

Wikipedia の定義によると、

mixin とはオブジェクト指向プログラミング言語において、サブクラスによって継承されることにより機能を提供し、単体で動作することを意図しないクラスである。

とのことなので、そもそも Mixin される側の実装と結びつきが強くなるのは仕方ないのだろう。しかし、まれにだが破れ紙の片割れのような実装を持つ Mixin ができてしまうことがある。

一枚の紙を適当に手で破って二枚にする。断面はギザギザだが、破った二枚の紙の断面はぴったり一致する。もともと繋がっていたものを切り離したのだから当然だ。だが破れ紙の断面にぴったり合うような別の紙を用意するのは簡単ではない。別の紙を合わせる必要があるのならば、紙を破るときにそれを見越して断面がギザギザしないように注意深く破っておかなければならない(実際には、破るのではなくハサミかカッターで切って断面が綺麗になるようにすべきだろう)

ダメな Mixin は破れ紙の片割れに似ている。 Mixin される側と合わさって初めて用をなすが、断面(インタフェース)がギザギザに入り組んでいるので、新しく Mixin を適用したいクラスを作るのが非常に難しくなる。コードの再利用を促進するためにわざわざ別のパーツとして分割したのに、そのパーツの組み込み方が難しくて再利用したくてもできない、なんてことになりがちだ。

なぜそんな Mixn が出来上がってしまうかというと、もともとひとまとまりになっていた実装の一部を深く考えずに抜き出して Mixin へと分割してしまうからだ。ひとまとまりのコードを書いた本人はそれらのコードがどのように相互作用しあって動作するかよくわかっているので、分割後のコードもうまく脳内で組み合わせてすんなり読める。だから、他人の目からみると密結合していて分割できそうにないようなコードであっても分割できてしまうのだ。

不運なことに、継承や Mixin などはオブジェクト指向プログラミングにおいて一般に良い設計手法とみなされている。なので、上記のようなダメ設計・ダメ実装の Mixin であっても、「Mixin という良い手法を使っているのだからこれは良い設計・良い実装だ」という誤った大義名分が幅をきかせやすい。ちょっと無茶だな、と思っても、そういう感覚は個人の感性・センスによる部分が大きいので言語化しづらく、したがってレビューでも指摘しづらい。「手法は良いがお前の設計・実装が悪い」と単刀直入に(しかも相手の気分をなるべく害さず)言うのは難しい(特に英語だと)。

これはあくまで特定の Mixin の設計・実装が悪いだけであって、 Mixin という手法そのものには利点がたくさんある。そこに異論はない。だが、 Mixin は便利で強力な分、再利用性と可読性の高い Mixin を設計・実装するには相応の能力やセンスが求められる。にもかかわらず能力不足だったりセンスが悪いひとの手によるイマイチな Mixin が作られてしまってうやむやのままコードベースに根を張ってしまうのは避けたい。という悩みが冒頭の「Mixin 避けるべきでは」という疑問につながる。

ではどうすればよいかというと、移譲が使えるのなら移譲すればよく、そうでなければ再利用を諦めてコピペするのも悪くないのでは、という気がしている。そもそも汎用的に使えるほど一般化したユースケースを見極められていないからインタフェースが破れ紙のギザギザになってしまうわけで、そういうコードは無理やり引き剥がさず混然一体とした状態のままにしておくほうがよいのではないか。経験上そういう箇所は変更の機会が多いので、ほどなくして「似ているが異なるもの」として分岐していくことが多いし、時間が経ってもずっと分岐せず重複したままだったらその時点で改めて分割したほうがよりよい切り離し方ができるはずだ。