継承と合成ではなく、いつ特性を使用するのですか?


9

OOPに関して再利用性を実装するには、AFAIKの3つの一般的な方法があります。

  1. 継承:通常はis-a関係(アヒルis-a鳥)を表す
  2. 構成:通常、関係があることを表す(車はエンジンがある)
  3. 特性(例:PHPの特性キーワード):...これについて本当にわからない

トレイトはhas-aとis-aの両方の関係を実装できるように思えますが、どのようなモデリングが意図されていたのかはよくわかりません。どのような状況のために特性が設計されましたか?


あなたは特性についてもう少し説明するかもしれませんが、特性は単に作曲の別の言葉かもしれません?
Trilarion 2016年


1
その理由も知りたいです。一度も使ったことがない。
アンディ

回答:


5

特性は、合成を行う別の方法です。それらは、コンパイル時に(またはJITコンパイル時に)クラスのすべての部分を構成し、必要な部分の具体的な実装を組み立てる方法と考えてください。

基本的に、さまざまな機能の組み合わせでクラスを作成しているときに、特性を使用します。この状況は、他の人が使用できるように柔軟なライブラリを作成している人々にとって最も頻繁に発生します。たとえば、ここにScalaTestを使用して最近書いた単体テストクラスの宣言があります

class TestMyClass
  extends WordSpecLike
  with Matchers
  with MyCustomTrait
  with BeforeAndAfterAll
  with BeforeAndAfterEach
  with ScalaFutures

ユニットテストフレームワークを持ってトン異なる設定オプションのを、そしてすべてのチームは、彼らは物事をやりたい方法について異なる嗜好を持っています。オプションをトレイト(withScala での使用に混合されている)に入れることにより、ScalaTestはWordSpecLikeWithMatchersAndFutures、のようなクラス名やのような大量のランタイムブールフラグを作成する必要なく、それらすべてのオプションを提供できますWordSpecLike(enableFutures, enableMatchers, ...)。これにより、オープン/クローズの原則に従うことが容易になります。新しい特性を追加するだけで、新しい機能や機能の新しい組み合わせを追加できます。また、普遍的に必要とされていない関数をトレイトに簡単に配置できるため、インターフェイス分離の原則に従うのが容易になります。

トレイトは、継承階層を共有する意味がない複数のクラスに共通コードを配置するための良い方法でもあります。継承は非常に密接に結びついた関係であり、それを助けることができればそのコストを支払うべきではありません。特性は、はるかに疎結合の関係です。上記の例では、MyCustomTrait他の関係のないいくつかのテストクラス間でモックデータベース実装を簡単に共有していました。

依存性注入は同じ目標の多くを達成しますが、プログラマー入力に基づくコンパイル時ではなく、実行時はユーザー入力に基づいて実行されます。トレイトは、意味的に同じクラスの一部である依存関係を対象としています。あなたは、あるクラスのパーツを組み立てるようなもので、他の責任を持つ他のクラスを呼び出すのではありません。

依存性注入フレームワークは、プログラマーの入力に基づいてコンパイル時に同じ目標の多くを達成しますが、適切な特性のサポートのないプログラミング言語の主な回避策です。トレイトは、これらの依存関係をコンパイラのタイプチェッカーの領域に持ち込みます。構文がより簡潔で、ビルドプロセスがよりシンプルなので、コンパイル時の依存関係とランタイムの依存関係をより明確に区別できます。


こんにちは、これは素晴らしい答えです。トレイトをいつ使用し、依存性注入をいつ使用するかをどのように決定するかを尋ねてもよいでしょうか。同じことを達成しているようです。
Extrakun 2016年

正確な観察。私の編集を参照してください。
カールビーレフェルト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.