質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

Q&A

解決済

4回答

8125閲覧

mixinではなぜプロパティを設定してはいけないのでしょうか? 多重継承で後から読み込む内容を有効にしてはいけない?

re97

総合スコア208

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

0グッド

2クリップ

投稿2016/04/05 00:08

編集2016/04/06 00:53

mixin
・仕組みの名称ではなく、メソッドのみを持つクラスのこと?
・プロパティを持ったらなぜ駄目なのでしょうか?


多重継承の問題点
・衝突した場合実装も含めて後から読み込んだ内容で必ず上書きする、というルールを加えても駄目なのでしょうか?
・何が問題?
・多重継承する際、スーパークラスそれぞれに優先順位は付けられないのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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
raccy

総合スコア21733

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2016/04/07 14:07

Pythonは優先順位付き多重継承をサポートしているのですね。 使うことが可能なものであるという事実にびっくりです。
re97

2016/04/08 00:22

・回答ありがとうございました ・言語間の挙動の違いについて具体的に言及されていたので大変参考になりました ・各言語によってそれぞれ思想の違いがあり、面白いなと感じました >mixinと似たような事ができるけど、mixinではないものもある >Scalaのtraitは名前に反して、トレイトではなくmixinです ・言語間で微妙に用語の言い回しが異なるので、複数言語を実際に試さないと理解できないな、と思いました >優先順位付き多重継承 ・「どうして出来ないんだろう」と思っていたのですが、出来る言語もあると知り、疑問が解決しました
guest

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
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

re97

2016/04/07 02:36

・回答ありがとうございました ・内容難しかったです ・ダイヤモンド継承の問題で、例えばプロパティを持たずにメソッドだけを継承してもこの問題は生じるのでしょうか? ・プロパティがないメソッドだけのクラスを作成しても継承するメリットは薄い? ・多重継承は祖先が絡むから問題が生じるということでしょうか? ・例えばですが、「複数クラスから継承」かつ「継承は1回だけ」という概念を表す言葉はあるでしょうか? ・「複数クラスから継承」できたら便利だと思うのですが ・「継承1回」では使用しづらいので問題あり?
Chironian

2016/04/07 07:53

> ・内容難しかったです 私自身、基底クラスを独立させた時のデメリットを今一把握できていないので明確に回答できていないと思います。すいません。 > ・ダイヤモンド継承の問題で、例えばプロパティを持たずにメソッドだけを継承してもこの問題は生じるのでしょうか? 生じないと思います。というか、それが正にMixinのようです。 > ・プロパティがないメソッドだけのクラスを作成しても継承するメリットは薄い? そのメソッドが何を操作するのか?が問題ですね。staticメソッドでは意味ないですし。 Mixinの発祥はRubyと聞きますので、Rubyに詳しい方ならメリットを語ってくれるかも知れません。そのような質問を投げればきっと回答を得ることができると思います。 > ・多重継承は祖先が絡むから問題が生じるということでしょうか? 多重継承の最大の問題はそこと思います。 > ・例えばですが、「複数クラスから継承」かつ「継承は1回だけ」という概念を表す言葉はあるでしょうか? 私は知らないです。ないような気がします。 > ・「複数クラスから継承」できたら便利だと思うのですが C++は多重継承できます。 JavaやC#等、C++を参考にしつつ生産性の高い言語が多数開発されていますが、それらは使い方が難しい機能をばっさり削ってます。学習コストを下げるためと思います。 学習コストを下げたい言語がダイヤモンド継承できてしまうのは、やはりまずいように思います。 > ・「継承1回」では使用しづらいので問題あり? 1回だけ多重継承できても良いような気もしますね。 でも、Ruby、Java、C#はobjectクラスがルートにあります。ということは多重継承すると全てダイヤモンド継承になります。 全てのクラスが1つのルートを持つという設計はそれなりに有用ですので、継承1回よりそちらを選択したということかも知れません。
re97

2016/04/08 00:01

・コメントありがとうございました ・大変参考になりました
guest

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
Favi_ty

総合スコア42

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

re97

2016/04/06 00:53

・回答ありがとうございました ・JavaScriptタグは外しました ・一般的に「mixin」「多重継承」と言えば、どうなるのでしょうか? ・言語によって異なるのでしょうか? ・多重継承する際、優先順位は付けられない?
guest

0

わからない事を並べ立てるのではなく、自分から調べて得た知識を元に分からない内容を狭い範囲で質問する事をお勧めします。
厳しいようですが、このままでは何も調べてなくて質問しているようにしか読めません。

一ついえることは多重継承に明らかな問題点はありません。
ECMAScript の prototype-chain が多重継承ですし、使い方次第だと私は思います。

Re: re97 さん

投稿2016/04/05 14:14

編集2016/04/05 14:15
think49

総合スコア18156

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問