mixin
・仕組みの名称ではなく、メソッドのみを持つクラスのこと?
・プロパティを持ったらなぜ駄目なのでしょうか?
多重継承の問題点
・衝突した場合実装も含めて後から読み込んだ内容で必ず上書きする、というルールを加えても駄目なのでしょうか?
・何が問題?
・多重継承する際、スーパークラスそれぞれに優先順位は付けられないのでしょうか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
ベストアンサー
###mixin
・仕組みの名称ではなく、メソッドのみを持つクラスのこと?
いいえ、仕組みの名前です。クラスにmixinするのは、module(Rubyの場合)やtrait(Scalaの場合)と言語によって違います。これらは一般的にインスタンス化できないという特徴を持つため、クラスとは区別されます。そして、クラスでは無い何かをクラスに組み込む(継承させる)という仕組み自体をmixinと読んでいます。mixinなる何かを作るわけではありません。
・プロパティを持ったらなぜ駄目なのでしょうか?
プロパティは持てます。Rubyであればmoduleでインスタンス変数を扱えますし、Scalaであればtariteでフィールドを定義できます。
Ruby
1module M0 2 def init(x = 0) 3 @value = x 4 end 5 def add(x) 6 @value += x 7 end 8 def value 9 @value 10 end 11end 12 13class C0 14 def name 15 "C0" 16 end 17end 18 19class C1 < C0 20 include M0 21 def sub(x) 22 @value -= x 23 end 24end 25 26test = C1.new 27test.init(0) 28test.add(3) 29puts test.value 30test.sub(1) 31puts test.value 32puts test.name
Scala
1trait M0 { 2 protected var value: Int = _ 3 def init(x: Int = 0) { 4 this.value = x 5 } 6 def add(x: Int) { 7 this.value += x 8 } 9 def getValue() = { 10 this.value 11 } 12} 13 14class C0 { 15 def name() = { 16 "C0" 17 } 18} 19 20class C1 extends C0 with M0 { 21 def sub(x: Int) { 22 this.value -= x 23 } 24} 25 26object MixMix { 27 def main(args: Array[String]) { 28 val test = new C1 29 test.init(0) 30 test.add(3) 31 println(test.getValue) 32 test.sub(1) 33 println(test.getValue) 34 println(test.name) 35 } 36}
注意すべきなのは、mixinと似たような事ができるけど、mixinではないものもあると言うことです。
- PHPのtraitは本物のトレイトです。平坦化されるためmixinではありません。トレイトはmixinと違い継承関係(is-a)を持ちません。(Scalaのtraitは名前に反して、トレイトではなくmixinです。)
- Javaでは、interfaceでdefault付きメソッドを定義することで、mixinに近い事が実現できます。しかし、interfaceは実装する(implement)ものであり、mixinではありませんし、フィールド定義ができないなどの制限があります。
- C#では、interfaceと拡張メソッドで、mixinに近い事が実現できます。しかし、これも、mixinと似たようなことをしたいときのテクニックであり、mixinではありませんし、フィールド定義ができないなどの制限があります。
- Pythonは多重継承ができるため、多重継承を使えばmixinと同じ事ができます。
###多重継承の問題点
・衝突した場合実装も含めて後から読み込んだ内容で必ず上書きする、というルールを加えても駄目なのでしょうか?
衝突時のルールを言語仕様としてきちんと決めていれば、駄目ではありません。例えば、Pythonの多重継承では探索順位を決め、衝突した場合でも、大丈夫のようにしています。(ただし、Pythonでは、後を上書きでは無く、前に定義されたものから探索になります)
・何が問題?
・多重継承する際、スーパークラスそれぞれに優先順位は付けられないのでしょうか?
Pythonでは厳密な探索順位によってどのメソッドが使われるかがきまるため、ダイヤモンド継承であっても問題ありません(Python3からは全てがobjectを先祖に持つため、多重継承すれば常にダイヤモンド継承になります)。また、インスタンス変数はクラスでは無くインスタンスに紐付けられるため、同じく問題にはなりません。Pythonのような動的型付き言語によるダックタイピングにおいては、ポリモーフィズムはメソッドの有無であり、同じく問題とされません。
Python
1class A: 2 def a(self): 3 return "a" 4class B0(A): 5 def b(self): 6 return "b0" 7class B1(A): 8 def b(self): 9 return "b1" 10class C(B0, B1): 11 def c(self): 12 return "c" 13x = C() 14print(x.a()) 15print(x.b()) 16print(x.c())
では、問題として出てくるのはなぜか?それはC++における、静的型付けでのポリモーフィズムと構造体を拡張した素朴な実装では問題が発生するからです。
C++
1#include <iostream> 2class A 3{ 4public: 5 const char *a() 6 { 7 return "a"; 8 } 9}; 10class B0 : public A 11{ 12public: 13 const char *b() 14 { 15 return "b0"; 16 } 17}; 18class B1 : public A 19{ 20public: 21 const char *b() 22 { 23 return "b1"; 24 } 25}; 26class C : public B0, public B1 27{ 28public: 29 const char *c() 30 { 31 return "c"; 32 } 33}; 34int main() 35{ 36 C x; 37 std::cout << x.a() << std::endl; 38 std::cout << x.b() << std::endl; 39 std::cout << x.c() << std::endl; 40}
上のコードはPythonとほぼ同じ事をしているはずなのにコンパイルエラーになります。まず、x.b()
がエラーになるのは、C++ではPythonのように親クラスに優先順位が無いため、B0とB1のどちらを使えばいいのかわからないからです。x.a()
も同じです。C++では、B0経由のAとB1経由のAの二つのAが存在し、a()も二つ持つことになります。これもどちらを使えば良いのかわからないため、エラーになります。ただ、キャストして型を明示すれば一応解決できます。例えばstatic_cast<B0>(x).b()
とすればいいのです。他にも、virtualを使うなどでAを一つだけにする方法があります。
このようにC++での失敗をみた多くの人たちは多重継承を避けることにしました。Pythonのように厳密な優先順位により問題を解決した言語もありますが、Javaのように単一継承にして複雑性を避けるという言語の方法が多いようです。ただ、どちらも一長一短であり、どちらが優れているかというのはあまりないように思います。
投稿2016/04/07 12:47
編集2016/04/07 14:27総合スコア21739
0
こんにちは。
・衝突した場合実装も含めて後から読み込んだ内容で必ず上書きする、というルールを加えても駄目なのでしょうか?
・何が問題?
結局、ダイヤモンド継承が引き起こす問題が残ってしまうと思います。
###ダイヤモンド継承
例えばC#では全てのクラスがobjectクラスを継承しています。ユーザが定義したクラスも暗黙的に継承しています。その御蔭で、objectがToString()を持っているので、どのクラスもToString()を持ってますのでなかなか便利です。
さて、仮に多重継承できたと仮定し、クラスfooとbarを継承するbazを定義したとします。
fooもbarもobjectを継承していますので、bazはobjectをfoo経由とbar経由で継承しています。
このような継承図を描くと菱型になるので、ダイヤモンド継承と呼びます。
###ダイヤモンド継承の問題
そして、objectがstatusというプロパティを持っていたとします。(実際にはないですが。)
bazがfooのメソッドhoge()を呼び出した結果statusが変更されたとします。すると、barにとってはあずかり知らないところでstatusが変更されてしまいます。自分の基底クラスのプロパティが自分が知らない内に変わってしまうのです。
これはもうパニックです。こんなことまで考慮してプログラムを作れるプログラマは少ないのではないでしょうか?
###多重継承してもダイヤモンド継承にならないようにする?
・多重継承する際、スーパークラスそれぞれに優先順位は付けられないのでしょうか?
多重継承した時、ダイアモンド継承にならないよう、fooとbarがそれぞれ別のobjectを持ち、bazが使うobjectはfooとbarの優先順位の高い方(仮にfooとします)にしたと仮定します。
すると、ポリモーフィズムが破綻します。bazインスタンスをobject変数に入れた場合、それはfooでもありますが、barではないです。bazの基底クラスの1つで参照しているにも関わらず、bazの基底クラスであるbarの機能を持たないわけです。
実装上のパニックどころか、設計的にパニックになりそうに感じます。
実際にそのような言語を作って、使うためのノウハウを積めばもしかすると使えるアイデアなのかも知れないですが、個人的にはパニックが酷くて使えない言語になりそうな予感がします。
といいますか、世の中には頭の良い人が多数いますし、多重継承の問題は多くの人が取り組んでいますので、既に実際に実装し、役に立たないことが確認されているからメジャーになっていないということかも知れません。
【追記】
継承はis_a関係ですので、fooはobjectの一種であり、barとbazもそうです。
従って、bazはobjectの一種として振る舞います。そして、bazはbarの一種としても振る舞います。
優先順位を付けた場合、bazはfooのobjectの一種として振る舞うが、barのobjectとしては振る舞わない、でも、barとしては振る舞うことになりますね。
「継承」という概念ではない、何か他のパラダイムになりそうです。
投稿2016/04/06 02:19
編集2016/04/06 02:43総合スコア23272
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/04/07 07:53
2016/04/08 00:01
0
恐らく、厳密な理論に基づいた回答がすぐに必要というわけではなさそうだと受け取りました。
なのでざっくりとした私見による回答をします。
・仕組みの名称ではなく、メソッドのみを持つクラスのこと?
実はJavaScriptにはmixin
という名前の仕組みや機能はありません。
(厳密な意味ではClass
という機能もJavaScriptにはありません)
なので、最初の質問に対する回答は仕組みの名称でもメソッドを持つクラスのどちらでもないとなります。
JavaScriptでmixin
という場合はオブジェクト指向言語のmixinという概念を実現するためのイディオム、というのが実態に近いです。
つまり、mixin
と銘打ってる機能があるライブラリやアプリ毎にそれがどういう仕組みになっていて、どんな機能を持っているのかそれぞれ違ってる可能性が高いということです。
・プロパティを持ったらなぜ駄目なのでしょうか?
JavaScriptのオブジェクトはプロパティしか持っていません。メソッドはないんです。
メソッドだと捉えているものは、実際には関数が値として格納されているプロパティです。
なのでJavaScriptでmixin的なものを実装する場合も**「駄目と言われてもコレしかないんだよ!気を付けて使え!」**的な感じのものになってる事が多いです。
多重継承の問題点
最後にこちらについてですが、JavaScriptでは多重継承はそもそも行えないので、今はあまり深く考えなくて良い問題だと思います。
(Mixinが機能として提供されてる言語もMixinを使う場合は多重継承にならないので、やはり考えなくて大丈夫なことが多いと思います)
もし、JavaScript以外の多重継承可能な言語における問題点に興味があるようでしたらその言語のタグを付与した別の質問を作成したほうが良いと思います。
投稿2016/04/05 15:33
編集2016/04/05 15:46総合スコア42
0
わからない事を並べ立てるのではなく、自分から調べて得た知識を元に分からない内容を狭い範囲で質問する事をお勧めします。
厳しいようですが、このままでは何も調べてなくて質問しているようにしか読めません。
一ついえることは多重継承に明らかな問題点はありません。
ECMAScript の prototype-chain が多重継承ですし、使い方次第だと私は思います。
Re: re97 さん
投稿2016/04/05 14:14
編集2016/04/05 14:15総合スコア18189
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/04/07 14:07
2016/04/08 00:22