ruby mixinの使いどころ、設計的な考え方

株式会社ジラフ
3 min readNov 22, 2018

--

とある社内のエンジニアのmixinの使い方に関する一言からある議論になりました。

オブジェクト指向設計では、オブジェクト同士の関係を is a の関係であるか、 has a 関係であるかを考えながら設計していくと良い設計になります。

この is ahas a を表現するために用いられる手法が、あります。代表的なものとしては、

is a: クラス継承
has a: コンポジションによる委譲

をそれぞれ使用して表現するのが一般的です。

しかし、rubyにはこれら両方の性質をもった mixin というクラスをmodule単位で切り出して、適時合成する機能があります。
初めてrubyを触った時は、mixinをどのように扱うべきか悩みました。

mixin moduleは、includeするとクラス自体に合成されるため、クラス内のメンバ変数へのアクセスや、別のモジュールのメソッドを呼び出すことができるなど、かなり自由度の高い実装が可能で便利な機能です。
反面、何も考えずに書いてしまうと、メンテナンス性の低いモジュールが量産されてしまうことになります。
では、どのようにmixinを設計すべきなのでしょうか?

基本的に委譲を意識したmixinがよい

よくあるmixinのコードです。

class Dog
include Runnable
end
class Cat
include Runnable
end
module Rannable
def run
end
end
Dog.new.run

同じコードをコンポジションを用いた委譲の形で書くとこうなります。

class Dog
def initialize
@leg = Leg.new
end
def run
@leg.run
end
end
class Leg
def run
end
end
Dog.new.run

そして、継承で書いてみます

class Dog < Leg
end
class Leg
def run
end
end
Dog.new.run

典型的な継承の失敗例になってしまっていますね。
やはり、has aを意識するほうがよさそうです。
実際にDHHなども ***able というconcernをたくさん作っていますね
https://twitter.com/dhh/status/964244090224128001

結論: mixinは has a 関係を意識すると良い

***ableのような名前で命名する
・include元の実装に依存しない
・ include元のメンバ変数は参照しない
・ include元の関数は呼ばない
というのを気をつけるとよいmixinの設計ができるのではないでしょうか!

リアルワールドではこれらを破った方が効率的なケースもあるので、そういう意味で柔軟性の高い仕様になっているのかと思います。
なので、理由が説明できれば、上記のようなルールから外れても良いと思います。

異議あり!という方や、こういった設計のお話が好きな方がいらっしゃいましたら、弊社ではエンジニアを募集中ですのでぜひお気軽にご連絡ください!カジュアル面談だけでもお待ちしております!

--

--