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

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

ただいまの
回答率

88.34%

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

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 4,228

re97

score 208

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


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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+2

mixin 

・仕組みの名称ではなく、メソッドのみを持つクラスのこと? 

いいえ、仕組みの名前です。クラスにmixinするのは、module(Rubyの場合)やtrait(Scalaの場合)と言語によって違います。これらは一般的にインスタンス化できないという特徴を持つため、クラスとは区別されます。そして、クラスでは無い何かをクラスに組み込む(継承させる)という仕組み自体をmixinと読んでいます。mixinなる何かを作るわけではありません。

・プロパティを持ったらなぜ駄目なのでしょうか?

プロパティは持てます。Rubyであればmoduleでインスタンス変数を扱えますし、Scalaであればtariteでフィールドを定義できます。

module M0
  def init(x = 0)
    @value = x
  end
  def add(x)
    @value += x
  end
  def value
    @value
  end
end

class C0
  def name
    "C0"
  end
end

class C1 < C0
  include M0
  def sub(x)
    @value -= x
  end
end

test = C1.new
test.init(0)
test.add(3)
puts test.value
test.sub(1)
puts test.value
puts test.name
trait M0 {
  protected var value: Int = _
  def init(x: Int = 0) {
    this.value = x
  }
  def add(x: Int) {
    this.value += x
  }
  def getValue() = {
    this.value
  }
}

class C0 {
  def name() = {
    "C0"
  }
}

class C1 extends C0 with M0 {
  def sub(x: Int) {
    this.value -= x
  }
}

object MixMix {
  def main(args: Array[String]) {
    val test = new C1
    test.init(0)
    test.add(3)
    println(test.getValue)
    test.sub(1)
    println(test.getValue)
    println(test.name)
  }
}

注意すべきなのは、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のような動的型付き言語によるダックタイピングにおいては、ポリモーフィズムはメソッドの有無であり、同じく問題とされません。

class A:
    def a(self):
        return "a"
class B0(A):
    def b(self):
        return "b0"
class B1(A):
    def b(self):
        return "b1"
class C(B0, B1):
    def c(self):
        return "c"
x = C()
print(x.a())
print(x.b())
print(x.c())

では、問題として出てくるのはなぜか?それはC++における、静的型付けでのポリモーフィズムと構造体を拡張した素朴な実装では問題が発生するからです。

#include <iostream>
class A
{
public:
    const char *a()
    {
        return "a";
    }
};
class B0 : public A
{
public:
    const char *b()
    {
        return "b0";
    }
};
class B1 : public A
{
public:
    const char *b()
    {
        return "b1";
    }
};
class C : public B0, public B1
{
public:
    const char *c()
    {
        return "c";
    }
};
int main()
{
    C x;
    std::cout << x.a() << std::endl;
    std::cout << x.b() << std::endl;
    std::cout << x.c() << std::endl;
}

上のコードは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 23:07

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

    キャンセル

  • 2016/04/08 09:22

    ・回答ありがとうございました
    ・言語間の挙動の違いについて具体的に言及されていたので大変参考になりました
    ・各言語によってそれぞれ思想の違いがあり、面白いなと感じました

    >mixinと似たような事ができるけど、mixinではないものもある
    >Scalaのtraitは名前に反して、トレイトではなくmixinです
    ・言語間で微妙に用語の言い回しが異なるので、複数言語を実際に試さないと理解できないな、と思いました

    >優先順位付き多重継承
    ・「どうして出来ないんだろう」と思っていたのですが、出来る言語もあると知り、疑問が解決しました

    キャンセル

+1

恐らく、厳密な理論に基づいた回答がすぐに必要というわけではなさそうだと受け取りました。
なのでざっくりとした私見による回答をします。

・仕組みの名称ではなく、メソッドのみを持つクラスのこと? 

実はJavaScriptにはmixinという名前の仕組みや機能はありません。
(厳密な意味ではClassという機能もJavaScriptにはありません)

なので、最初の質問に対する回答は仕組みの名称でもメソッドを持つクラスのどちらでもないとなります。

JavaScriptでmixinという場合はオブジェクト指向言語のmixinという概念を実現するためのイディオム、というのが実態に近いです。

つまり、mixinと銘打ってる機能があるライブラリやアプリ毎にそれがどういう仕組みになっていて、どんな機能を持っているのかそれぞれ違ってる可能性が高いということです。

・プロパティを持ったらなぜ駄目なのでしょうか?

JavaScriptのオブジェクトはプロパティしか持っていません。メソッドはないんです。
メソッドだと捉えているものは、実際には関数が値として格納されているプロパティです。

なのでJavaScriptでmixin的なものを実装する場合も「駄目と言われてもコレしかないんだよ!気を付けて使え!」的な感じのものになってる事が多いです。

多重継承の問題点

最後にこちらについてですが、JavaScriptでは多重継承はそもそも行えないので、今はあまり深く考えなくて良い問題だと思います。
(Mixinが機能として提供されてる言語もMixinを使う場合は多重継承にならないので、やはり考えなくて大丈夫なことが多いと思います)

もし、JavaScript以外の多重継承可能な言語における問題点に興味があるようでしたらその言語のタグを付与した別の質問を作成したほうが良いと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/04/06 09:53

    ・回答ありがとうございました
    ・JavaScriptタグは外しました

    ・一般的に「mixin」「多重継承」と言えば、どうなるのでしょうか?
    ・言語によって異なるのでしょうか?
    ・多重継承する際、優先順位は付けられない?

    キャンセル

+1

こんにちは。

・衝突した場合実装も含めて後から読み込んだ内容で必ず上書きする、というルールを加えても駄目なのでしょうか? 
・何が問題? 

結局、ダイヤモンド継承が引き起こす問題が残ってしまうと思います。

ダイヤモンド継承

例えば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/07 11:36

    ・回答ありがとうございました
    ・内容難しかったです

    ・ダイヤモンド継承の問題で、例えばプロパティを持たずにメソッドだけを継承してもこの問題は生じるのでしょうか?
    ・プロパティがないメソッドだけのクラスを作成しても継承するメリットは薄い?

    ・多重継承は祖先が絡むから問題が生じるということでしょうか?
    ・例えばですが、「複数クラスから継承」かつ「継承は1回だけ」という概念を表す言葉はあるでしょうか?
    ・「複数クラスから継承」できたら便利だと思うのですが
    ・「継承1回」では使用しづらいので問題あり?

    キャンセル

  • 2016/04/07 16:53

    > ・内容難しかったです

    私自身、基底クラスを独立させた時のデメリットを今一把握できていないので明確に回答できていないと思います。すいません。

    > ・ダイヤモンド継承の問題で、例えばプロパティを持たずにメソッドだけを継承してもこの問題は生じるのでしょうか?

    生じないと思います。というか、それが正にMixinのようです。

    > ・プロパティがないメソッドだけのクラスを作成しても継承するメリットは薄い?

    そのメソッドが何を操作するのか?が問題ですね。staticメソッドでは意味ないですし。
    Mixinの発祥はRubyと聞きますので、Rubyに詳しい方ならメリットを語ってくれるかも知れません。そのような質問を投げればきっと回答を得ることができると思います。

    > ・多重継承は祖先が絡むから問題が生じるということでしょうか?

    多重継承の最大の問題はそこと思います。

    > ・例えばですが、「複数クラスから継承」かつ「継承は1回だけ」という概念を表す言葉はあるでしょうか?

    私は知らないです。ないような気がします。

    > ・「複数クラスから継承」できたら便利だと思うのですが

    C++は多重継承できます。
    JavaやC#等、C++を参考にしつつ生産性の高い言語が多数開発されていますが、それらは使い方が難しい機能をばっさり削ってます。学習コストを下げるためと思います。

    学習コストを下げたい言語がダイヤモンド継承できてしまうのは、やはりまずいように思います。

    > ・「継承1回」では使用しづらいので問題あり?

    1回だけ多重継承できても良いような気もしますね。

    でも、Ruby、Java、C#はobjectクラスがルートにあります。ということは多重継承すると全てダイヤモンド継承になります。
    全てのクラスが1つのルートを持つという設計はそれなりに有用ですので、継承1回よりそちらを選択したということかも知れません。

    キャンセル

  • 2016/04/08 09:01

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

    キャンセル

0

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

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

Re: re97 さん

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

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