" Programming Python "で、Mark Lutzは "mixin"について言及しています。私はC/C++/C#のバックグラウンドから来ました、そして私は前に用語を聞いたことがありません。ミックスインとは何ですか?
この例 (これはかなり長いのでリンクしています)の行間を読んで、 '適切な'サブクラス化ではなく、クラスを拡張するために多重継承を使用するケースだと思います。これは正解?
新しい機能をサブクラスに入れるのではなく、なぜそれをしたいのでしょうか。それについては、なぜミックスイン/多重継承のアプローチがコンポジションを使用するよりも優れているのでしょうか。
多重継承からミックスインを分けるものは何ですか?それは単なる意味論の問題でしょうか。
ミックスインは特別な種類の多重継承です。ミックスインが使用される主な状況は2つあります。
一番の例としては、 werkzeugの要求と応答のシステム を考えてください。次のようにして、わかりやすい要求オブジェクトを作成できます。
from werkzeug import BaseRequest
class Request(BaseRequest):
pass
Acceptヘッダーのサポートを追加したいのであれば、
from werkzeug import BaseRequest, AcceptMixin
class Request(AcceptMixin, BaseRequest):
pass
Acceptヘッダー、etags、認証、およびユーザーエージェントのサポートをサポートするリクエストオブジェクトを作成したい場合は、次のようにします。
from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin
class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
pass
違いは微妙ですが、上記の例では、mixinクラスは独立していません。より伝統的な多重継承では、(例えば)AuthenticationMixin
はおそらくAuthenticator
のようなものになるでしょう。つまり、クラスは自立するように設計されているでしょう。
まず、ミックスインは多重継承言語にしか存在しないことに注意してください。 JavaやC#ではミックスインはできません。
基本的に、ミックスインは独立した基本型で、制限された機能と子クラスに対する多型の共鳴を提供します。 C#で考えているのであれば、すでに実装されているので実際に実装する必要がないインターフェースを考えてください。あなたはそれから継承し、その機能から利益を得るだけです。
ミックスインは一般的に範囲が狭く、拡張されることを意味しません。
[編集 - その理由は:]
私はあなたが尋ねたので、私はなぜ対処しなければならないと思います。大きな利点は、あなたがそれを何度も自分でやる必要がないということです。 C#では、ミックスインが恩恵を受ける可能性がある最大の場所は 処分パターン です。あなたがIDisposableを実装するときはいつでも、あなたはほとんど常に同じパターンに従うことを望みます、しかしあなたはわずかな変化で同じ基本的なコードを書いてそして書き直すことになってしまいます。拡張可能な処分ミックスインがあれば、余分なタイピングを省くことができます。
[編集2 - 他の質問に答える]
多重継承からミックスインを分けるものは何ですか?それは単なる意味論の問題でしょうか。
はい。ミックスインと標準多重継承の違いは、単なるセマンティクスの問題です。多重継承を持つクラスは、その多重継承の一部としてミックスインを利用するかもしれません。
ミックスインのポイントは、継承型に影響を与えずに継承を介して他の型に「ミックスイン」できる型を作成しながら、その型に有益な機能を提供することです。
繰り返しますが、既に実装されているインターフェースを考えてください。
私は主にそれらをサポートしない言語で開発するので私は個人的にmixinを使用しません、それで私はまさにその「ああ」を供給するまともな例を考え出すことが本当に困難な時を過しています。あなたのために瞬間。しかし、もう一度やります。私は人為的な例を使用するつもりです - ほとんどの言語はすでに何らかの形で機能を提供しています - しかしそれは、うまくいけば、mixinがどのように作られ使用されるべきかを説明するでしょう。これが行きます:
XMLとの間でシリアル化できるタイプがあるとします。型に、その型のデータ値を含むXMLフラグメントを含む文字列を返す "ToXML"メソッドと、文字列内のXMLフラグメントから型がデータ値を再構築できるようにする "FromXML"を提供するとします。繰り返しますが、これは人為的な例ですので、おそらくファイルストリーム、またはあなたの言語のランタイムライブラリからのXML Writerクラスを使ってください。重要なのは、オブジェクトをXMLにシリアル化して、XMLから新しいオブジェクトを取り戻すことです。
この例のもう1つの重要な点は、これを一般的な方法で行いたいということです。直列化したいすべての型に対して "ToXML"と "FromXML"メソッドを実装する必要はありません。型がこれを確実に実行するための一般的な方法が欲しいのです。あなたはコードを再利用したいのです。
あなたの言語がそれをサポートしているならば、あなたはあなたのためにあなたの仕事をするためにXmlSerializable mixinを作成することができます。この型はToXMLメソッドとFromXMLメソッドを実装します。この例では重要ではない何らかのメカニズムを使用して、ToXMLから返されるXMLフラグメントを作成するために混在させるあらゆるタイプから必要なすべてのデータを収集することができ、FromXMLを使用する場合も同様にそのデータを復元できますと呼ばれる。
以上です。これを使用するには、XmlSerializableから継承したXMLにシリアル化する必要がある任意の型があります。その型をシリアライズまたはデシリアライズする必要があるときはいつでも、単にToXMLまたはFromXMLを呼び出すことになります。実際、XmlSerializableは本格的な型であり、多相型であるため、元の型については何も知らないドキュメントシリアライザを構築することもできます。たとえば、XmlSerializable型の配列だけを受け入れます。
このシナリオを他のことに使うことを想像してみてください。それを混ぜ合わせるすべてのクラスがすべてのメソッド呼び出しをログに記録するようにするmixin、またはそれを混ぜ合わせるタイプにトランザクション性を提供するmixinの作成など。
ミックスインを、そのタイプに影響を与えずに少量の機能をタイプに追加するように設計された小さな基本タイプと考えるだけでいいのであれば、問題はありません。
うまくいけば。 :)
この答えはmixin と例 を説明することです:
自己完結型 :short。例を理解するためにライブラリを知る必要はありません。
Pythonでは 、他の言語ではありません。
Rubyのような他の言語からの例がそれらの言語ではるかに一般的であるので、そこにあったことは理解できますが、これはPythonスレッドです。
それはまた論争の的になる質問を考慮します:
多重継承はミックスインを特徴付けるために必要かどうか?
定義
私はまだ「権威ある」情報源から、Pythonのミックスインとは何かを明確に述べている引用を見たことがありません。
私はミックスインの2つの可能な定義を見ました(それらが抽象基底クラスのような他の同様の概念と異なると考えられるべきであるなら)、そして人々はどれが正しいかについて全く同意しません。
コンセンサスは言語によって異なります。
定義1:多重継承なし
ミックスインは、そのクラスのいくつかのメソッドがそのクラスで定義されていないメソッドを使用するようなクラスです。
したがって、このクラスはインスタンス化されることを意図しているのではなく、基本クラスとして機能します。そうでなければ、インスタンスは例外を発生させずに呼び出すことができないメソッドを持つでしょう。
ある情報筋が付け加えた制約は、クラスはデータだけではなくメソッドだけを含むかもしれないということですが、なぜこれが必要なのかわかりません。しかし実際には、多くの便利なミックスインにはデータがなく、データのない基本クラスの方が使いやすいです。
典型的な例は<=
と==
のみからのすべての比較演算子の実装です。
class ComparableMixin(object):
"""This class has methods which use `<=` and `==`,
but this class does NOT implement those methods."""
def __ne__(self, other):
return not (self == other)
def __lt__(self, other):
return self <= other and (self != other)
def __gt__(self, other):
return not self <= other
def __ge__(self, other):
return self == other or self > other
class Integer(ComparableMixin):
def __init__(self, i):
self.i = i
def __le__(self, other):
return self.i <= other.i
def __eq__(self, other):
return self.i == other.i
assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)
# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o
この特定の例はfunctools.total_ordering()
デコレータによって達成されたかもしれません、しかしここのゲームは輪を再発明することでした:
import functools
@functools.total_ordering
class Integer(object):
def __init__(self, i):
self.i = i
def __le__(self, other):
return self.i <= other.i
def __eq__(self, other):
return self.i == other.i
assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)
定義2:多重継承
ミックスインは基本クラスのあるメソッドが定義していないメソッドを使うデザインパターンであり、そのメソッドは定義1のような派生ではなく他の基本クラスによって実装されることを意図しています。
mixin classという用語は、そのデザインパターンで使われることを意図している基本クラスを指します(メソッドを使うもの、それを実装するもの)。
特定のクラスがミックスインかどうかを判断するのは簡単ではありません。メソッドは派生クラスに実装するだけでよく、その場合は定義1に戻ります。作者の意図を考慮する必要があります。
このパターンは、基本クラスをさまざまに選択して機能を再結合することができるため、興味深いものです。
class HasMethod1(object):
def method(self):
return 1
class HasMethod2(object):
def method(self):
return 2
class UsesMethod10(object):
def usesMethod(self):
return self.method() + 10
class UsesMethod20(object):
def usesMethod(self):
return self.method() + 20
class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass
assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22
# Nothing prevents implementing the method
# on the base class like in Definition 1:
class C3_10(UsesMethod10):
def method(self):
return 3
assert C3_10().usesMethod() == 13
信頼できるPythonの出現回数
collections.abc の公式文書)では、この文書では明示的にMixin Methodsという用語を使用しています。
それはクラスならば:
__next__
を実装しますIterator
から継承それからクラスは無料で__iter__
mixin methodを取得します。
したがって、少なくともドキュメントのこの点では、 mixinは多重継承 を必要とせず、定義1と整合性があります。
ドキュメントはもちろん異なる点で矛盾する可能性があり、他の重要なPythonライブラリはそれらのドキュメントで他の定義を使用しているかもしれません。
このページではSet mixin
という用語も使用されています。これは、Set
やIterator
などのクラスをMixinクラスと呼ぶことができることを明確に示しています。
他の言語の場合
Ruby: Programming Ruby およびThe Ruby programming Language)などの主要な参考書に記載されているように、明らかにmixinに多重継承を必要としません。
C++:実装されていないメソッドは純粋仮想メソッドです。
定義1は抽象クラス(純粋仮想メソッドを持つクラス)の定義と一致します。そのクラスはインスタンス化できません。
定義2は仮想継承で可能です: 2つの派生クラスからの多重継承
私はそれらを多重継承を使用する規則的な方法として考えています - 最終的にはミックスインはミックスインと呼ばれるクラスに関する規約に従うかもしれない別のpythonクラスに過ぎないからです。
あなたがミックスインと呼ぶものを支配する慣習の私の理解はミックスインということです:
object
からのみ継承されます。こうすることで、多重継承の潜在的な複雑さを制限し、(完全な多重継承と比べて)見なければならない場所を制限することによってプログラムの流れを追跡することをかなり簡単にします。それらはRubyモジュールに似ています。
インスタンス変数を追加したいのであれば(単一継承で許される以上の柔軟性を持って)、私は合成に行きがちです。
そうは言っても、私はインスタンス変数を持っているXYZMixinと呼ばれるクラスを見ました。
ミックスインはプログラミングの概念で、クラスは機能を提供しますが、インスタンス化に使用されることを意図したものではありません。 Mixinの主な目的は、独立した機能を提供することです。ミックスイン自体が他のミックスインとの継承を持たず、状態を回避するのであれば、それが最善です。 Rubyなどの言語では、直接的な言語サポートがいくつかありますが、Pythonの場合はそうではありません。ただし、Pythonで提供されている機能を実行するために、マルチクラス継承を使用することができます。
私はこのビデオを見ました http://www.youtube.com/watch?v = v_uKI2NOLEM ミックスインの基本を理解するために/。初心者にとって、ミックスインの基本とそれらがどのように機能するか、そしてそれらを実装する際に直面するかもしれない問題を理解することは非常に役に立ちます。
ウィキペディアはまだ最高です: http://en.wikipedia.org/wiki/Mixin
多重継承からミックスインを分けるものは何ですか?それは単なる意味論の問題でしょうか。
ミックスインは多重継承の限られた形式です。一部の言語では、ミックスインをクラスに追加するためのメカニズムは、継承のメカニズムと(構文の点で)わずかに異なります。
特にPythonでは、mixinはサブクラスに機能を提供する親クラスですが、それ自体をインスタンス化することは意図されていません。
「ミックスインではなく多重継承ではない」と言うのは、ミックスインと混同される可能性のあるクラスを実際にインスタンス化して使用できるからです。つまり、これは意味論的な、そして非常に現実的な違いです。
この例、 ドキュメントの はOrderedCounterです。
class OrderedCounter(Counter, OrderedDict): 'Counter that remembers the order elements are first encountered' def __repr__(self): return '%s(%r)' % (self.__class__.__name__, OrderedDict(self)) def __reduce__(self): return self.__class__, (OrderedDict(self),)
Counter
モジュールからのOrderedDict
とcollections
の両方をサブクラス化します。
Counter
とOrderedDict
はどちらもインスタンス化され、独自に使用されることを意図しています。しかし、それらを両方ともサブクラス化することで、順番に並べられ、各オブジェクトのコードを再利用するカウンタを持つことができます。
これはコードを再利用するための強力な方法ですが、問題もある可能性があります。いずれかのオブジェクトにバグがあることが判明した場合、注意しないで修正すると、サブクラスにバグが発生する可能性があります。
ミックスインは通常、OrderedCounterのように協調的な多重継承が持つ可能性のある結合の問題なしにコードを再利用する方法として推奨されています。ミックスインを使用するときは、データと密接に関連していない機能を使用します。
上記の例とは異なり、mixinは単独で使用することを意図していません。それは新しいまたは異なる機能を提供します。
たとえば、標準ライブラリのsocketserver
ライブラリには mixinがいくつかあります 。
これらのミックスインクラスを使用して、各タイプのサーバーのフォークバージョンとスレッドバージョンを作成できます。たとえば、ThreadingUDPServerは次のように作成されます。
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
UDPServerで定義されたメソッドをオーバーライドするため、ミックスインクラスが最初に来ます。さまざまな属性を設定すると、基盤となるサーバーメカニズムの動作も変わります。
この場合、mixinメソッドは、同時実行を可能にするためにUDPServer
オブジェクト定義内のメソッドをオーバーライドします。
オーバーライドされたメソッドはprocess_request
であるように見え、それはまた別のメソッド、process_request_thread
を提供します。これは ソースコード :からのものです。
class ThreadingMixIn: """Mix-in class to handle each request in a new thread.""" # Decides how threads will act upon termination of the # main process daemon_threads = False def process_request_thread(self, request, client_address): """Same as in BaseServer but as a thread. In addition, exception handling is done here. """ try: self.finish_request(request, client_address) except Exception: self.handle_error(request, client_address) finally: self.shutdown_request(request) def process_request(self, request, client_address): """Start a new thread to process the request.""" t = threading.Thread(target = self.process_request_thread, args = (request, client_address)) t.daemon = self.daemon_threads t.start()
これは主にデモンストレーションを目的としたmixinです - ほとんどのオブジェクトはこのreprの有用性を超えて進化します。
class SimpleInitReprMixin(object):
"""mixin, don't instantiate - useful for classes instantiable
by keyword arguments to their __init__ method.
"""
__slots__ = () # allow subclasses to use __slots__ to prevent __dict__
def __repr__(self):
kwarg_strings = []
d = getattr(self, '__dict__', None)
if d is not None:
for k, v in d.items():
kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
slots = getattr(self, '__slots__', None)
if slots is not None:
for k in slots:
v = getattr(self, k, None)
kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
return '{name}({kwargs})'.format(
name=type(self).__name__,
kwargs=', '.join(kwarg_strings)
)
そして使用法は次のようになります。
class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
__slots__ = 'foo',
def __init__(self, foo=None):
self.foo = foo
super(Foo, self).__init__()
そして使用法:
>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)
それ以外の方法(継承ではなく合成、あるいは単にメソッドを自分のクラスにモンキーパッチするなど)が見付かる場合は、新しいPythonコードのミックスインに対してお勧めします。努力。
古いスタイルのクラスでは、他のクラスからいくつかのメソッドを取得する方法としてミックスインを使用できます。しかし、新しいスタイルの世界では、ミックスインも含めて、すべてがobject
から継承されます。つまり、多重継承を使用すると当然 MRO issues が導入されます。
多重継承MROをPythonで動作させる方法、とりわけsuper()関数がありますが、それはあなたがsuper()を使用してあなたのクラス階層全体をしなければならないことを意味し、そして制御の流れを理解することはかなり難しいです。
私はここでいくつかの良い説明があったと思いますが、私は別の見方を提供したいと思いました。
Scalaでは、ここで説明したようにミックスインを行うことができますが、非常に興味深いのは、ミックスインが実際に一緒に「融合」されて、継承する新しい種類のクラスが作成されることです。本質的には、複数のクラス/ミックスインから継承するのではなく、継承するミックスインのすべてのプロパティを持つ新しい種類のクラスを生成します。 Scalaは多重継承が現在サポートされていない(Java 8の時点で)JVMに基づいているので、これは理にかなっています。ちなみに、このmixinクラス型はScalaのTraitと呼ばれる特別な型です。
それは、クラスが定義される方法で示唆されています:クラスNewClassは、FirstMixinとSecondMixinをThirdMixinに拡張します。
CPythonインタプリタが同じこと(mixinクラス構成)をしているかどうかはわかりませんが、私は驚かないでしょう。また、C++の背景から来た場合、私はABCやmixinに相当する「インターフェース」とは呼ばないでしょう。これは似たような概念ですが、使用方法や実装方法が異なります。
おそらくいくつかの例が役に立つでしょう。
クラスを構築していて、それを辞書のように振る舞わせたい場合は、必要なさまざまな__ __
メソッドをすべて定義できます。しかし、それは少し苦痛です。別の方法として、いくつか定義して、(他の継承に加えて)UserDict.DictMixin
から継承することができます(py3kではcollections.DictMixin
に移動)。これは辞書apiの残りすべてを自動的に定義する効果があります。
2番目の例:GUIツールキットwxPythonを使用すると、複数の列を持つリストコントロールを作成できます(たとえば、Windowsエクスプローラのファイル表示など)。デフォルトでは、これらのリストはかなり基本的なものです。 ListCtrlから継承して適切なミックスインを追加することで、列ヘッダーをクリックして特定の列でリストをソートする機能などの追加機能を追加できます。
これはPythonの例ではありませんが、 Dプログラミング言語 では、 mixin
という用語は、ほぼ同じ方法で使用される構造を指すために使用されます。たくさんのものをクラスに追加する。
D(これはちなみにMIを行いません)では、これはテンプレートを構文に挿入することによって行われます(構文的に安全で安全なマクロを考えれば、近いと思います)。これにより、クラス、構造体、関数、モジュール、またはその他何でも1行のコードで複数の宣言に展開できます。
Rubyの例が役に立つかもしれません:
Mixin Comparable
をインクルードして1つの関数"<=>(other)"
を定義することができます、mixinはそれらすべての関数を提供します:
<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)
これは<=>(other)
を呼び出して正しい結果を返すことによって行われます。
両方のオブジェクトが等しい場合は"instance <=> other"
は0を返し、instance
がother
より大きい場合は0未満、other
がより大きい場合は0を超えます。
mixinはクラスに機能を追加する方法を提供します、すなわち、あなたは望ましいクラスの中にモジュールを含めることによってモジュールで定義されたメソッドと対話することができます。 Rubyは多重継承をサポートしていませんが、それを実現するための代替手段としてmixinを提供しています。
これは、mixinを使用して多重継承を実現する方法を説明する例です。
module A # you create a module
def a1 # lets have a method 'a1' in it
end
def a2 # Another method 'a2'
end
end
module B # let's say we have another module
def b1 # A method 'b1'
end
def b2 #another method b2
end
end
class Sample # we create a class 'Sample'
include A # including module 'A' in the class 'Sample' (mixin)
include B # including module B as well
def S1 #class 'Sample' contains a method 's1'
end
end
samp = Sample.new # creating an instance object 'samp'
# we can access methods from module A and B in our class(power of mixin)
samp.a1 # accessing method 'a1' from module A
samp.a2 # accessing method 'a2' from module A
samp.b1 # accessing method 'b1' from module B
samp.b2 # accessing method 'a2' from module B
samp.s1 # accessing method 's1' inside the class Sample
私はただpython milterのための単体テストを実装するためにpythonミックスインを使いました。通常、熟練者がMTAと話し合い、単体テストを難しくします。テストミックスインはMTAと対話するメソッドをオーバーライドし、代わりにテストケースによって駆動されるシミュレート環境を作成します。
そのため、spfmilterのような変更されていないmilterアプリケーションと、TestBaseを次のようにmixinします。
class TestMilter(TestBase,spfmilter.spfMilter):
def __init__(self):
TestBase.__init__(self)
spfmilter.config = spfmilter.Config()
spfmilter.config.access_file = 'test/access.db'
spfmilter.spfMilter.__init__(self)
次に、milterアプリケーションのテストケースでTestMilterを使用します。
def testPass(self):
milter = TestMilter()
rc = milter.connect('mail.example.com',ip='192.0.2.1')
self.assertEqual(rc,Milter.CONTINUE)
rc = milter.feedMsg('test1',sender='[email protected]')
self.assertEqual(rc,Milter.CONTINUE)
milter.close()
http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup
私はあなたがC#の背景を持っていることを読みました。だから良い出発点は、.NETのmixin実装でしょう。
http://remix.codeplex.com/ でcodeplexプロジェクトをチェックアウトすることをお勧めします。
概要を得るためにlang.netシンポジウムリンクを見てください。 codeplexページのドキュメントにはまだまだたくさんの情報があります。
ステファンについて
OPは、彼/彼女がC++でmixinを聞いたことは一度もないと言った、おそらくそれはC++で奇妙に繰り返されるテンプレートパターン(CRTP)と呼ばれるからだろう。 @Ciro Santilli氏は、mixinはC++の抽象基底クラスを介して実装されているとも述べました。抽象基本クラスはmixinを実装するために使用できますが、実行時の仮想テーブル検索のオーバーヘッドなしにコンパイル時にテンプレートを使用して実行時の仮想関数の機能を実現できるため、やり過ぎです。
CRTPパターンは詳しく説明されています ここ
以下のテンプレートクラスを使用して、@ Ciro Santilliの回答のPythonの例をC++に変換しました。
#include <iostream>
#include <assert.h>
template <class T>
class ComparableMixin {
public:
bool operator !=(ComparableMixin &other) {
return ~(*static_cast<T*>(this) == static_cast<T&>(other));
}
bool operator <(ComparableMixin &other) {
return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
}
bool operator >(ComparableMixin &other) {
return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
}
bool operator >=(ComparableMixin &other) {
return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
}
};
class Integer: public ComparableMixin<Integer> {
public:
Integer(int i) {
this->i = i;
}
int i;
bool operator <=(Integer &other) {
return (this->i <= other.i);
}
bool operator ==(Integer &other) {
return (this->i == other.i);
}
protected:
ComparableMixin() {}
};
int main() {
Integer i(0) ;
Integer j(1) ;
//ComparableMixin<Integer> c; // this will cause compilation error because constructor is protected.
assert (i < j );
assert (i != j);
assert (j > i);
assert (j >= i);
return 0;
}
編集:継承可能でインスタンス化できないように、ComparableMixinに保護コンストラクタを追加しました。 ComparableMixinのオブジェクトが作成されたときに保護されたコンストラクタがコンパイルエラーを発生させる方法を示すために例を更新しました。