fladdict.net/blogさんの「OOP全盛期だけど、手続き型がマイブームになってる(前)」を読んで、んー、これってMix-in的なあれなんだろうか、とかうだうだ考えていたんだけど、ちゃんと考える時間がなくなったんで、とりあえずメモだけして寝ることに。
OOP全盛期だけど、手続き型がマイブームになってる(前)
http://www.fladdict.net/blog-jp/archives/2005/08/oop.php
じゃあ、なんで「手続き型」がいいのかというと、手続き型だと他人のライブラリを導入するときや、自分のライブラリを再設計するときに、既存資産の運用がく柔軟にできるのではないかと思ったりしたんです。
oo的な方は、Colorの実装の継承をしてColorHSBを。Colorの実装の継承をしてColorGrayを。んで、両方を利用できる新しいクラスを作りたい、と。でも内容的に多重継承はNGなんだろうな。
で、これを手続き型の方で解消している手段として、ColorUtil.setHSBの中にColorHSBクラス的な処理を閉じ込めていると。
そして、ここからが面白い。
で、ここからが応用。
「手続き型」は関数の呼び出し作業が煩雑なのがネックなわけですが、これをOOPで解消するっていうのはどうでしょう?手続き型関数ColorUtilを作成した上で、Colorクラスを拡張してColorHsbを作ります。そして、ColorHsbのsetHSB()の処理では、内部的に ColorUtil.setHSB()を呼び出すように処理するわけです。
function setHSB(h, s, b){
ColorUtil(this, h, s, b);
}こうすると、ユーザー的には手続きを意識しないでOOPで作業ができます。またColorGrayのような他人のクラスであっても、 ColorGrayを拡張して内部でColorUtilにバイパスしてやれば、ColorGrayにsetHSBを実装するのも一瞬ですみます。超楽チン。変に多重継承するわけでもないので、オブジェクトツリーがレガシーで埋まることもなく、自分のライブラリをブラッシュアップとかするのも凄い楽になります。
こうすると、OOPの利便性を機能を保ちながら、オブジェクトとアルゴリズムを分離できるんじゃないかと思ったわけっす。処理は微妙に重くなるけど、最近のパソなら大丈夫っしょ。
これって、ちょっと実装上は違っているけど、発想は完全にRubyのMix-inですよね?
「なんだ車輪の再発明じゃん。」ていう意味ではなくて、自分で考えてここまで来ているってすごくないですか?
私は答え(Mix-in)を先に知ってしまったので、あー、なるほど。うんうん。というかんじでしたが、Ruby&Mix-inを知らずに何もヒントなしにこれを自分で応用できたかというと疑問です。(いつかは何かを読んでたどり着いたかもしれませんが)
読みながら純粋に感動してました。
で、Ruby的な模擬コードを書いてみたので、書いておく。
class Color
def setRGB(r, g, b)
end
end
class ColorHSB < Color # オリジナルの拡張クラス・Colorの実装を継承
def setHSB(h, s, b)
end
end
こいつが元々の状態。
で、ColorGrayが出てくる。
class ColorGray < Color # ライブラリの再利用・Colorの実装を継承
def foo
end
def var
end
end
このsetHSBの手続き型プログラミング的な解法をRubyのMix-inを利用してやると、こんな風にまとまる。
module ColorUtil # これがMix-inモジュール
def setHSB(h, s, b)
# 実装上は、Colorクラスの仕様継承(同じAPI)のものはなんでもOKな実装をする。
# >他人が作ったColorGrayクラスであろうとも、その親クラスがColorクラスである限り、
# >apiを介してrgb値にアクセスするならばなんの問題もないわけです。
# の通りだ。
end
end
class Color
def setRGB(r, g, b)
end
end
class ColorHSB < Color # オリジナルの拡張クラス・Colorの実装を継承(Mix-in改良版)
include ColorUtil # ColorUtilのMix-in。ColorUtilの実装を混ぜ込み継承。
end
class ColorGray < Color # ライブラリの再利用・Colorの実装を継承
include ColorUtil # ColorUtilのMix-in。ColorUtilの実装を混ぜ込み継承。
def foo
end
def var
end
end
これでどうなるかというと、ColorHSBのインスタンスからもColorGrayのインスタンスからもsetHSB
が呼び出せるようになる。
わかりやすいようにここではColorHSBというクラスをそのまま拡張して作成したが、実際にRubyでプログラミングする場合はColorHSB
は作らずにColorクラスに追加でinclude
ColorUtil
だけしてやると思う。(ケースにもよるとは思うが。)
そうすると、Colorを継承しているColorGrayは自動的にsetHSB
を使えるようになる。
なんやかんやで最終的なイメージはこんなかんじ。
module ColorUtil
def setHSB(h, s, b)
p 'setHSB called !'
end
end
class Color
include ColorUtil
def setRGB(r, g, b)
end
end
class ColorGray < Color
def hoge
end
def var
end
end
<実行サンプル>
c = Color.new
c.setHSB 240,100,100
=> "setHSB called !"
g = ColorGray.new
g.setHSB 240,100,100
=> "setHSB called !"
てなかんじ。Love Ruby!
「OOP全盛期だけど、手続き型がマイブームになってる(後)」もあるようなので、期待してます。
# うわぁ、全然「メモだけして寝ることに」にならんかったー!