最近モチベダウンのmyugaruです。おはようございます。
気分転換に凪瀬さんとかずきさんにトラックバックしてみようと思います。
凪瀬さんの 実装を委譲できる言語を作ればいいのかもしれない より
「檜山正幸のキマイラ飼育記なんで多重継承はそんなに嫌われるのか? ちょっくら分析してみるかあたりからの着想
多重継承が~という話題は継承をインターフェースの継承だけにした上で、実装を委譲する機能を言語に持たせれば解決するかもしれない。」
引用されている「多重継承が~」という話を読んでみました。
多重継承を必要とするケースは世の中にあると私も思いました。
そして多重継承を使わずにどう解決しているか?に目を向けてみました。
まず現実問題として。
C#で考えるとインターフェースの多重実装は可能ですがクラスの多重継承はできません。
C#で委譲が優遇されていないことはかずきさんが確認されたとおりです。([Ruby][Java][C#]丸投げ委譲)
では多重継承はC#では不要なのかというと話は違うと思います。
先に書きましたが、それを必要とするケースは世の中にあると思うのです。
少し考えてきて私はそれはコンポーネント技術で置き換えられないか?と発想してみました。
次のフォームを見ると、
2つのボタンは2つのタブに乗っています。
それら2つのタブがフォームへさらに乗っています。
これをボタンのダイアモンド継承基底クラスが同じ場合の多重継承を表すと考えてみます。
(11:50:↑επιστημηさんの本を読んだはずなのに・・・シャノンさんの御指摘により修正しました。以降も同様の修正あり)
継承・委譲というとボタンだったら継承・委譲後もボタンになるべきでしょう。
上の図で言えばタブはタブのままでありボタンを継承や委譲してると言えません。
しかし機能の量に注目しますとタブはボタンを貼り付けたおかげでボタンの機能が増えました。
たとえばこれが継承・委譲だったら継承したボタンは元のボタンの機能に新しい機能を追加します。ているでしょう。
(11:19:シャノンさんのご指摘により上の赤字部分を修正しました。紛らわしくてすみません。御指摘ありがとうございました)
どちらも機能量は元のボタンの機能+αとなります。
なので両者は機能面で同等の問題を解決できる手段になりうると考えても間違いじゃないと思います。
凪瀬さんが書いているダイアモンド継承基底クラスが同じ場合の多重継承でのどちらの親の実装を使うべきか問題は、
ここに見えるどちらのボタンを押すべきか?という問題に置き換えられるかと思います。
両方見せておいてユーザーが必要な方のボタンを押します。
実際には両者の内容を識別できるようなラベルが書いてあるべきでしょう。
多重継承をコンポーネント技術の話に全て置き換えられるか?
と問い詰められるともう少し踏み込みが必要でしょう。
私がコンポーネント技術を推すのはIDEによって多くの恩恵が受けられるメリットや、
そもそもC#などの言語で多重継承がなくなった経緯に関係があるのでは?
など色々と考えての勝手な推論です。
継承・委譲・コンポーネント技術の違いを入力の流れで書きます。
継承・委譲の入力は子から親へ伝わります。
ユーザーからの入力は子が受けます。
親へはbase呼び出しなどで処理を中継して伝えていきます。
追加機能は中継時に前処理や後処理で記述します。
コンポーネントの入力は親から子へ伝わります。
ユーザーからの入力は親にあたるコンポーネントに入ります。
子であるコンテナにはイベントによって処理を中継します。
追加機能は後処理や前処理に該当するイベントハンドラーを作って記述します。
●具体的にコードを提示してみます
これはかずきさんが例とされていたものと同等です。
+=を書くとイベントハンドラーが自動実装されます。
先に書いたとおりイベントのサポートにはどの言語のIDEも多大に力が注がれているでしょう。
VSでもコード入力の手間はeclipseでの委譲実装並みだと思いますがどうでしょうか?
実際私はほとんど頭文字+tabとかで入力ができました。
ここでは後処理のハンドラーを実装してみました。
PiyoからhogeへアクセスするHogeプロパティもあわせて実装しています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Com.Wankuma.Kazuki.Deleg
{
class Program
{
static void Main(string[] args)
{
}
}
class Hoge
{
public event Action AfterFoo;
public event Action AfterBoo;
public void Foo()
{
Console.WriteLine("Hoge#Foo");
AfterFoo();
}
public void Boo()
{
Console.WriteLine("Hoge#Boo");
AfterBoo();
}
}
class Piyo
{
private Hoge hoge = new Hoge();
public Hoge Hoge { get { return hoge; } }
public Piyo()
{
hoge.AfterFoo += new Action(hoge_AfterFoo);
hoge.AfterBoo += new Action(hoge_AfterBoo);
}
void hoge_AfterBoo()
{
}
void hoge_AfterFoo()
{
}
}
}
●問題点
たぶん継承・委譲よりもイベントによる処理は重いと考えられます。
でもC#に限って言えば多重継承は実質不可能ですので
可読性を落とすような裏技的なコーディングスタイルをとるよりは
素直にコンポーネントに頼った方がよさそうに思います。