委譲か継承か、それが問題だ

この記事は 2013/4/1 に旧ブログの “Syn の独り言” で記載したものを
加筆、修正した者です。

なんか、「動的型付け言語では継承はダメ、委譲を使え」みたいな意見を聞いた。
いや、Rails だって、model は ActiveRedord 継承して作るじゃん。
お前、David さんディスってんのかよ

まあ、冗談は置いといて、
その人の周りに継承を使いすぎる人がいたのかもしれない。
僕が彼の意見を曲解しているのかもしれない。
でも、ちょっと気になったので今の僕の考えをまとめてみた。

まず、話のスコープをはっきりさせよう。
「オブジェクト指向」というと、少し範囲が広すぎる。
今回は、Java, Python, Ruby 等を前提に考えてみる。

僕の意見はこうだ。

まず、オブジェクト指向とかプログラムとか置いといて物作り一般に
広く言える事だが、

大きい物を作りたければ、疎結合で抽象化されている小さいパーツに分けろ。

疎結合とはパーツ外部からパーツ内部に対して与える影響が少ない状態の事。
逆に、影響が多い状態は密結合。

抽象化の定義を書くと色々と長くなるが、簡単に言うと
「抽象化されている」とは「直感的に使える」くらいに考えて欲しい。

人間、一度に多くの事を考える事は苦手だ。
また、頭のなかに有る作業用メモリが溢れると途端に難しくなる。
大きい = 複雑 = 難しい
くらいに考えて良いだろう。

そこで、疎結合で抽象化された小さいパーツに分けるのだ。
疎結合で小さければ、各パーツを作っている時にそのパーツだけに専念出来る。

IT のシステムに置いてパーツを疎結合にするには、パーツが外部とやり取りをする
インターフェースで外部から内部に出来る事を制限すれば良い。
(ここで言うインターフェースとは、Java の interface ではなく
外部との接続口という意味。)
インターフェースによる制限が厳しいほどパーツは疎結合になり、
制限が緩いほどパーツは密結合になる。
また、このインターフェースが直感的であれば、そのパーツは抽象化されていると
言えるだろう。

パーツの内部のやり取りの制限は緩くてよい。
というか、内部のやり取りを厳しく制限出来るならば、
さらに小さいパーツに分ける方が良いだろう。
(もちろん、パーツが充分小さければ分けないという選択肢もあり得るが。)

具体例を上げよう。
例えば、ある処理には速度向上のためにバッファリングが必要な場合。

その処理自体を行う為のパーツを用意したとして、バッファリングはどこで行うか?
今回は、バッファリングをあるその処理を行うパーツ自体に持たせる事を考えよう。
この為にはバッファをそのパーツ内部に持ち、そのバッファを扱う
インターフェースを用意しなければ良い。

上記の制限を行うと、どうなるか?
まず、パーツ内部を実装する際に「外部からバッファを操作される」という可能性を
考えなくて良くなる。
また、外部からこのパーツを使用する際もバッファリングという難しい処理を忘れて
直感的に使えるようになる。
内部の実装時も、外部の実装時も同時に考える項目数を減らす事ができるのだ。

後でバッファリングに関する部分を改修する時にも、この事は役に立つだろう。
改修時にはパーツ内部だけ考えれば良い。
なぜなら外部ではバッファリングの事を考えなくても良いようにしたから。

もちろん、制限を加えた事によって悪い事も発生する。
今回の場合、外部からはバッファリングの細かいチューニングが出来なくなった。
この事による速度の低下が致命的な場合は、この制限を加えたのは
間違いだったのだろう。
前の方に書いたが、「可能な範囲で」疎結合にするのだ。

さて、ずいぶん前置きが長くなってしまったがここからクラスの話に戻る。

クラスとは、プログラムにおけるパーツの一種。

「一種」と書いたのは、パーツの分け方が一通りでは無いから。
(ファイルを分けたり、関数で分けたり。)
多くの場合、public method がクラスの内部と外部のやり取りをする為の
インターフェースとなる。

では、クラスの委譲、継承とは何か?
どちらも、小さいクラスから大きいクラスを作る方法である。
両者の違いは小さいクラスに対する大きいクラスのアクセスの方法にある。

委譲は小さいクラスへ、そのインターフェースを通してアクセスする。
継承は小さいクラスの内部へ直接アクセスする。

委譲のメリットは、何と言っても小さいクラスと大きいクラスの疎結合性を
保てる事だ。
小さいクラスで外部と疎結合にするインターフェースを実装できるならば
小さいクラスと大きいクラスが疎結合になる。

逆にデメリットは、実装する必要のあるインターフェースが増える事だろう。
委譲の場合、大きいクラスでは、外部に公開するインターフェースを
全て実装しなくてはいけない。
たとえ、ほとんど同じインターフェースが小さいクラスにあったとしてもだ。
また、小さいクラスでは大きいクラスが必要とするインターフェースを
全て実装しなくてはいけない。

では、継承のメリットは?
それは、実装時のステップ数が減る事だ。
小さいクラスではインターフェースを実装する必要は無い。
インターフェースを含めて小さいクラスで実装済みの物は、ほとんど全て
大きいクラスで流用できる。
一つの子クラスを複数のクラスで流用すれば、全体のステップ数が減る。

継承のデメリットは、親クラスと子クラスが密結合になる事だ。
親クラスがどんなに優れたインターフェースを持っていたとしても、
子クラスはそのインターフェースを使わないので密結合になる。

では委譲と継承はどうやって使い分けたら良いだろう?

継承は「異なるクラスの共通部分を外出しして DRY にする」為に使う。

Interface

上手において、class A と class B は青色の部分が共通している。
それだったら、青色部分を外出しして共通の親クラスとすれば DRY になる。

外出ししたクラス(親クラス)は、パーツとして必要な機能を全て
実装しているわけではない。
(Java の抽象クラスなんかが、その典型。)
そういう意味で、「小さいクラス」だ。
継承は、小さいパーツに不足している機能を付け足し、肥大化させる事で
大きい(必要な機能を全て揃えた)パーツを作る。

委譲は「クラスをより小さい、疎結合なクラスに分割して実装する為」に使う。

委譲

上手のように、class A の中で青色部分と緑色部分に分かれており、
互いのやり取りの方法が限定されていたら?
そうやって疎結合な部分に分かれているのならば、
より小さいクラスに分けて実装する事で同時に考えなくては行けない事が
もっと減るかもしれない。
(青を実装中は緑を考えなくてよい。)

小さいクラスは、インターフェースを含めて小さいパーツに必要な物を
全て備えている。
委譲は、その小さいパーツを組み立てて一回り大きいパーツを作る。
委譲によって後で組み立てられる事が保証されているから、安心してクラスを細かく
分割できるのだ。

継承の本質は、重複を避けて DRY にする事だ。
複数クラスで重複した部分が無ければ、継承をする意味が無い。

委譲の本質は、大きいパーツをより小さい疎結合なパーツに分けて実装する事だ。
小さいクラスを複数のクラスで流用出来なかったとしても、委譲する意味はある。
(もちろん、複数クラスで小さいクラスを流用できるならば、
するに越した事は無い。)

GoF を初め、多くの人が「継承よりも委譲を」と言っているのは、
下記のような場合だと考えている。
委譲と継承

クラス A は、青い部分と緑の部分に疎結合に分ける事が出来る。
クラス B は、青い部分と赤い部分に疎結合に分ける事が出来る。

この場合、青のクラスを共通の親クラスとしてクラス A と B で継承で
実装する事もできる。
青のクラスを作ってクラス A と B で委譲で実装する事も出来る。
こういう場合は、委譲にするべきだろう。

継承か委譲か迷ったら、試しに青のクラスを作ってみると良い。
(これは、継承でも委譲でも、どのみち必要になる)
青のクラスが疎結合になる制限の強いインターフェースを含め、
パーツとして必要な機能を全て備えたクラスになった場合。
継承という方法で、その疎結合性を壊す必要は無い。委譲を使えば良い。
そうならなければ、継承を使う。

ついでに、緑と赤もパーツとして必要な機能を備えた、疎結合なクラスを
作れるのならば作ると良いだろう。
たとえ青の部分を継承で実装する事になったとしても。
たとえ緑と赤のクラスを他の部分で流用出来なかったとしても。

また、よく「is-A ならば継承を、has-A や use-A ならば委譲を使え」と言う。
これは、
「has-A や use-A の場合、A がロジック的に分かれているわけだから、
実装時も疎結合なクラス A として分けられる事が多い。
is-A ならば A がロジックとして分かれていないから、
実装時も疎結合なクラスに分けられない事が多い」
という事ではないか?
僕の経験としても、そうなっている事が多い。

さて、一番最初に話を戻して、僕が違和感を感じた
「動的型付け言語で継承はダメ、委譲を使え」という意見について。
彼は

Java のような静的型付けの言語では、親の型を引き継ぐ為に継承を使う事もあるだろう。
でも、Python のような動的型付け言語では型を引き継ぐ必要は無い。
ステップ数を減らしたいのはプログラマの怠慢であり、意味は無い。
疎結合にするため、委譲を使うべき。

という結論に至っていた。

それは違うだろう。
だいたい、Java だって Interface を使えば親のクラスの型を引き継ぐ必要はない。
ステップ数を減らすための怠慢は、優れたプログラマに必要な物だ。

彼は、
「委譲を使えば疎結合に、継承を使えば密結合になる」と考えているのではないか?

僕は、
「疎結合な小さいクラスを作ったという前提の元で、委譲を使うと
その疎結合性を保つ事ができる。
継承を使うと疎結合性を保つ事が出来ない」
つまり、
「もともと疎結合に分ける事ができなければ、
委譲にしても継承にしても疎結合にならない。」

「どうせ密結合になるなら、継承の方がステップ数が短くなるから
良いのではないか?」

と考えている。

もっとも、「どうせ密結合になるなら継承の方が良い」というのは、実は少し眉唾だ。
ただ、今回の話のスコープである、Java, Ruby, Python の場合は十分に正しいだろう。

最後に、僕が大きいシステムを作る際の作業フローを紹介してみる。

まず、僕は大きいシステムを一度に実装できるほど優れた人間ではない。
最初に、僕の手に負える程度のサイズまでシステムを疎結合なパーツに分ける。

何もクラスという形で分けるとは限らない。
小さいパーツは、より小さいシステムかもしれないし
「人間の目視確認」というワークフローかもしれない。
(人間による作業を増やすのは良い事じゃないけれど。)

また、パーツを分けたら今度は既存の物をそのパーツとして使用できないか考える。
既存のシステムとか、有名なミドルウェアとか、誰かが書いたライブラリとか、
プログラム言語のビルトインのシステムとか。
(というか、パーツ分けの段階で既にこの辺りは意識している。)

流用出来ない部分は、自分で実装するしかない。
自分の手に負える範囲まで小さく分けてから実装の開始だ。

各パーツを実装する際は、できるだけ DRY に、できるだけシンプルに行いたい。
多くの関数内で共通化できる部分があれば、その部分を外だしして別の関数を
作るだろう。
多くのクラスで共通化できる部分があれば、親クラスを1個作って継承させるだろう。

細かいパーツができたら、今度はそれを組み立てる。
ミドルウェアと組み合わせる場合はドライバを使うかもしれない。
システム同士を組み合わせる場合は API を使うかもしれない。

クラスとクラス、クラスと関数を組み合わせる場合は?
その時は委譲を使うだろう。
個々のクラスは外部と疎結合になっているはずなので、それを保ったまま
組み立てるのだ。

無事に組み上がってテストが通ったら、システムは完成する。

もっとも、上記のウォーターフォール的な流れを 1回行うだけで全て上手く行く事は
少ない。
先に大きいクラスを作ってから小さいクラスを作る場合も有る。
作りながらリファクタリンングを繰り返して、パーツを組み立ててからパーツを
再作成するかもしれない。
でも、何となく分かってもらえ無いだろうか?

ちなみに、多重継承はどんな時に使うだろう?
使うとしたら、上記の作業フローで言うと、単体継承と同じフェーズだ。
例えば、下図のようなクラスを作る場合。
多重継承

各クラスの同じ色の部分は同じ内容。
しかし、赤、青、緑、黄のそれぞれは密結合。
この場合、赤、青、黄、緑のそれぞれの部分をクラスとして実装してそれを
多重継承する方法もある。

ただ、自分でこれを実装するのは、非常にしんどい。
例えば、多重継承を使わない場合、クラス A の青い部分を実装している時は
青と緑の部分だけ考えれば良い。
でも、多重継承を前提に青いクラスを実装する場合は青、緑、赤を全て一度に考える
必要がある。

これを読む方は実装する人以上にしんどいだろう。
丁寧なドキュメントを用意する必要がある。

その他、多重継承のメソッド探索順序は非常に複雑になる場合も多い。
それについてはPython 菱形の継承にも書いたので良ければ見て欲しい。

多重継承を使用するのは、上記の労力を払ってでも実装する価値がある場合のみだ。
その多くは、汎用性の高いライブラリやフレームワーク、ビルトインや
標準ライブラリのクラスだろう。

代表的な例は Ruby だと思う。
正確に言うと Ruby は単体継承しかサポートしていないが、module を複数
include 出来る。
これは、実質的にはメソッド探索順序を単純化した多重継承みたいなもんだ。

Ruby の場合、多重継承もどき(include)を前提とした親クラスもどき(module)が
標準ライブラリにあふれているので、ユーザーも多重継承もどき(include)を
行う事も多い。
でも、普通は多重継承を使う事は滅多に無いんじゃないかな?

Author: wbcchsyn

未来のスーパーエンジニア とりあえず、新しい物が大好き

29 thoughts on “委譲か継承か、それが問題だ”

  1. I’m typically to running a blog and i actually respect your content. The article has really peaks my interest. I am going to bookmark your web site and keep checking for new information.

  2. I’m usually to blogging and i actually respect your content. The article has really peaks my interest. I’m going to bookmark your site and preserve checking for brand new information.

  3. You made some first rate factors there. I seemed on the internet for the problem and found most people will go along with together with your website.

  4. Oh my goodness! an amazing article dude. Thank you Nonetheless I’m experiencing subject with ur rss . Don抰 know why Unable to subscribe to it. Is there anybody getting similar rss problem? Anyone who is aware of kindly respond. Thnkx

  5. There are some fascinating time limits on this article however I don抰 know if I see all of them middle to heart. There is some validity but I will take maintain opinion until I look into it further. Good article , thanks and we want extra! Added to FeedBurner as nicely

  6. HELLOMYSORRY
    Thank you for the auspicious writeup. It in fact was a amusement account it. Look advanced to far added agreeable from you! By the way, how could we communicate?

  7. Oh my goodness! an incredible article dude. Thank you Nevertheless I’m experiencing situation with ur rss . Don抰 know why Unable to subscribe to it. Is there anyone getting equivalent rss drawback? Anyone who knows kindly respond. Thnkx

  8. After I originally commented I clicked the -Notify me when new feedback are added- checkbox and now every time a comment is added I get four emails with the same comment. Is there any way you possibly can remove me from that service? Thanks!

  9. After research a couple of of the weblog posts on your web site now, and I really like your method of blogging. I bookmarked it to my bookmark web site checklist and can be checking back soon. Pls take a look at my site as properly and let me know what you think.

  10. I am often to blogging and i really recognize your content. The article has really peaks my interest. I’m going to bookmark your site and preserve checking for new information.

  11. I discovered your blog site on google and examine a number of of your early posts. Proceed to maintain up the superb operate. I just additional up your RSS feed to my MSN News Reader. In search of forward to studying extra from you in a while!?

  12. Can I just say what a reduction to seek out someone who really knows what theyre talking about on the internet. You undoubtedly know how one can convey an issue to gentle and make it important. Extra folks need to learn this and understand this side of the story. I cant consider youre not more in style since you undoubtedly have the gift.

  13. Oh my goodness! an amazing article dude. Thanks Nonetheless I’m experiencing difficulty with ur rss . Don抰 know why Unable to subscribe to it. Is there anyone getting an identical rss downside? Anyone who is aware of kindly respond. Thnkx

  14. Spot on with this write-up, I really think this website wants far more consideration. I抣l in all probability be again to read way more, thanks for that info.

  15. I抎 have to test with you here. Which is not something I normally do! I enjoy studying a submit that will make individuals think. Additionally, thanks for permitting me to comment!

  16. A formidable share, I simply given this onto a colleague who was doing a little bit analysis on this. And he in reality purchased me breakfast as a result of I found it for him.. smile. So let me reword that: Thnx for the treat! However yeah Thnkx for spending the time to debate this, I feel strongly about it and love reading more on this topic. If doable, as you develop into expertise, would you thoughts updating your weblog with more particulars? It is extremely helpful for me. Big thumb up for this blog put up!

  17. Aw, this was a really nice post. In idea I would like to put in writing like this moreover ?taking time and precise effort to make an excellent article?however what can I say?I procrastinate alot and by no means seem to get something done.

  18. Can I simply say what a aid to search out someone who truly is aware of what theyre speaking about on the internet. You positively know the way to deliver an issue to mild and make it important. Extra people have to learn this and understand this facet of the story. I cant believe youre no more fashionable since you undoubtedly have the gift.

  19. After study a few of the weblog posts on your website now, and I actually like your manner of blogging. I bookmarked it to my bookmark website list and shall be checking back soon. Pls take a look at my website online as properly and let me know what you think.

  20. you could have a great blog here! would you like to make some invite posts on my blog?

  21. Youre so cool! I dont suppose Ive read anything like this before. So good to search out any individual with some unique thoughts on this subject. realy thank you for beginning this up. this website is something that is wanted on the web, somebody with somewhat originality. useful job for bringing something new to the internet!

Leave a Reply

コメントは反映されるまで数分かかります。
リンクのあるコメントはスパム判定されます。