複数のクラスが一連の規則と実装を順守しているということは、インターフェースのポイントではありませんか?
複数のクラスが一連の規則と実装を順守しているということは、インターフェースのポイントではありませんか?
回答:
厳密に言えば、そうではありません、YAGNIが適用されます。そうは言っても、インターフェイスの作成に費やす時間は最小限です。特に、ほとんどの作業を手軽に実行できる便利なコード生成ツールがある場合はそうです。のインターフェースが必要かどうか不明な場合は、インターフェースの定義をサポートする側で間違いを犯す方が良いと思います。
さらに、単一のクラスでさえインターフェースを使用すると、単体テスト用の別のモック実装が提供されます。これは実稼働環境ではありません。Avner Shahar-Kashtanの答えは、この点を拡張しています。
インターフェイスが必要かどうかは、それを実装するクラスの数に依存しないと答えます。インターフェイスは、アプリケーションの複数のサブシステム間のコントラクトを定義するためのツールです。本当に重要なのは、アプリケーションをサブシステムに分割する方法です。カプセル化されたサブシステムのフロントエンドとして、それらを実装するクラスの数に関係なく、インターフェースが必要です。
非常に便利な経験則の1つを次に示します。
Foo
を直接参照するようにするとBarImpl
、変更するFoo
たびに変更することを強くコミットしていますBarImpl
。基本的には、それらを2つのクラスに分割されるコードの1つのユニットとして扱います。Foo
がインターフェース を参照するようにするならBar
、あなたは代わりにあなたが自分自身をコミットして、あなたが変更する時への変更を避けFoo
ますBarImpl
。アプリケーションの重要なポイントでインターフェイスを定義する場合、サポートするメソッドとサポートしないメソッドを慎重に検討し、インターフェイスを明確にコメントして、実装の動作方法(および動作方法)を説明します。これらのコメント化されたインターフェイスは、アプリケーションの一種の仕様(動作の意図の説明)を提供するため、アプリケーションは非常に理解しやすくなります。これにより、コードの読み取りがはるかに簡単になります(「このコードが何をするのか」と尋ねる代わりに、「このコードはどうするのか」と尋ねることができます)。
これらすべてに加えて(または実際にそのため)、インターフェイスは個別のコンパイルを促進します。インターフェイスはコンパイルするのが簡単であり、実装よりも依存関係が少ないFoo
ため、インターフェイスを使用するクラスを記述すると、Bar
通常、再コンパイルBarImpl
する必要なく再コンパイルできますFoo
。大規模なアプリケーションでは、これにより多くの時間を節約できます。
Foo
インターフェイスに依存しBar
、次にBarImpl
再コンパイルすることなく変更することができますFoo
。4.パブリック/プライベートオファーよりもきめ細かいアクセス制御(異なるインターフェイスを介して2つのクライアントに同じクラスを公開します)。
If you make class Foo refer directly to class BarImpl, you're strongly committing yourself to change Foo every time you change BarImpl
変更する際に、Fooを使用して回避できる変更は何interface Bar
ですか?メソッドのシグネチャと機能がBarImplで変更されない限り、Fooはインターフェイスがなくても変更する必要はありません(インターフェイスの目的全体)。1つのクラスのみ、つまりBarImpl
Barを実装するシナリオについて説明しています。マルチクラスシナリオの場合、依存関係反転の原理と、インターフェイスがどのように役立つかを理解しています。
インターフェースは、動作、つまり関数/メソッドのプロトタイプのセットを定義するように指定されています。インターフェイスを実装する型はその動作を実装するので、そのような型を扱うとき、その動作を(部分的に)知っています。
インターフェイスによって定義された動作が1回だけ使用されることがわかっている場合、インターフェイスを定義する必要はありません。KISS(シンプルに、愚かにしてください)
理論的には、インターフェイスを保持するためだけにインターフェイスを使用するべきではありませんが、Yannis Rizosの答えは、さらなる複雑さを示唆しています。
ユニットテストを作成し、MoqやFakeItEasyなどのモックフレームワーク(使用した最新の2つに名前を付ける)を使用している場合、インターフェイスを実装する別のクラスを暗黙的に作成しています。コードまたは静的分析を検索すると、実装は1つだけであると主張される場合がありますが、実際には内部のモック実装があります。モックの作成を開始するたびに、インターフェースの抽出が理にかなっていることがわかります。
しかし、待ってください、まだあります。暗黙的なインターフェイスの実装が存在するシナリオは他にもあります。たとえば、.NETのWCF通信スタックを使用すると、リモートサービスへのプロキシが生成され、このプロキシが再びインターフェイスを実装します。
クリーンなコード環境では、ここでの残りの答えに同意します。ただし、インターフェイスを使用する可能性のあるフレームワーク、パターン、または依存関係に注意してください。
いいえ、必要ありません。クラス参照ごとにインターフェイスを自動的に作成するのはアンチパターンだと思います。
すべてのためにFoo / FooImplを作成するには、実際のコストがかかります。IDEはインターフェイス/実装を無料で作成する場合がありますが、コードをナビゲートしfoo.doSomething()
ているときは、実際の実装ではなく、インターフェイスシグネチャに移動する際にF3 / F12から余分な認識負荷がかかります。さらに、すべてのファイルを1つではなく2つのファイルで整理できます。
したがって、何かのために実際に必要な場合にのみ実行する必要があります。
次に反論に対処します。
依存性注入フレームワークのインターフェイスが必要です
フレームワークをサポートするインターフェースはレガシーです。Javaでは、以前は動的プロキシ、CGLIB以前の要件であったインターフェイス。今日、あなたは通常それを必要としません。EJB3、Springなどではもう必要ないのは、進歩と開発者の生産性の向上と考えられています。
単体テストにモックが必要です
独自のモックを作成し、2つの実際の実装がある場合、インターフェイスが適切です。コードベースにFooImplとTestFooの両方がある場合、最初にこの議論を行うことはおそらくないでしょう。
ただし、Moq、EasyMock、Mockitoなどのモックフレームワークを使用している場合は、クラスをモックでき、インターフェイスは必要ありません。foo.method = mockImplementation
メソッドを割り当てることができる動的言語での設定に似ています。
Dependency Inversion Principle(DIP)に従うインターフェイスが必要です
DIPは、実装ではなくコントラクト(インターフェイス)に依存するように構築すると言います。しかし、クラスはすでにコントラクトであり抽象化されています。それがpublic / privateキーワードの目的です。大学では、標準的な例はMatrixクラスまたはPolynomialクラスのようなものでした。消費者は、マトリックスを作成したり追加したりするためのパブリックAPIを持っていますが、マトリックスが疎または密な形式で実装されているかどうかは気にしません。その点を証明するために必要なIMatrixまたはMatrixImplはありませんでした。
また、DIPは多くの場合、主要なモジュール境界だけでなく、すべてのクラス/メソッド呼び出しレベルで過剰に適用されます。DIPを過度に適用している兆候は、1つではなく2つのファイルを変更する必要があるため、インターフェイスと実装がロックステップで変更されることです。DIPが適切に適用されている場合、インターフェイスを頻繁に変更する必要はありません。また、もう1つの兆候は、インターフェイスに実際のコンシューマー(アプリケーション)が1つしかないことです。多くの異なるアプリで使用するためのクラスライブラリを構築している場合は、話が異なります。
これは、ボブ・マーティンおじさんのモックについての論点です。主要なアーキテクチャの境界でモックするだけで済みます。webappでは、HTTPおよびDBアクセスが主要な境界です。間にあるすべてのクラス/メソッド呼び出しはそうではありません。DIPについても同様です。
こちらもご覧ください:
new Mockup<YourClass>() {}
すると、インターフェイス、抽象クラス、または具象クラスに関係なく、コンストラクタを含むクラス全体をモックできます。コンストラクターの動作を「オーバーライド」することもできます。MockitoまたはPowermockには同等の方法があると思います。
フェンスの両側の答えは次のように要約できるようです。
適切に設計し、インターフェイスが必要な場所にインターフェイスを配置します。
Yanniの回答への回答で述べたように、インターフェイスについて厳格で迅速なルールを持つことはできないと思います。ルールは、定義上、柔軟性が必要です。インターフェイスに関する私の規則は、APIを作成する場所であればどこでもインターフェイスを使用することです。そして、責任のあるドメインから別のドメインへと境界を越えている場所であればどこでもAPIを作成する必要があります。
(恐ろしく不自然な)例として、Car
クラスを構築しているとしましょう。クラスでは、確かにUIレイヤーが必要です。この特定の例では、それはの形をとるIginitionSwitch
、SteeringWheel
、GearShift
、GasPedal
、およびBrakePedal
。この車にはが入っているのでAutomaticTransmission
、必要はありませんClutchPedal
。(これはひどい車なので、エアコン、ラジオ、座席はありません。実際、フロアボードもありません。そのハンドルを握って最高の結果を期待するだけです!)
では、これらのクラスのどれがインターフェースを必要としますか?答えは、デザインによって異なりますが、すべてである場合もあれば、まったくない場合もあります。
次のようなインターフェイスを使用できます。
Interface ICabin
Event IgnitionSwitchTurnedOn()
Event IgnitionSwitchTurnedOff()
Event BrakePedalPositionChanged(int percent)
Event GasPedalPositionChanged(int percent)
Event GearShiftGearChanged(int gearNum)
Event SteeringWheelTurned(float degree)
End Interface
その時点で、これらのクラスの動作はICabin Interface / APIの一部になります。この例では、クラス(ある場合)はおそらくいくつかのプロパティと1つまたは2つの関数を備えた単純なものです。そして、設計で暗黙的に述べているのは、これらのクラスは、ICabinの具体的な実装をサポートするためだけに存在し、ICabin自体では存在できないか、ICabinコンテキスト外では意味がないということです。
プライベートメンバーの単体テストを行わないのと同じ理由です- パブリックAPIをサポートするためだけに存在するため、APIをテストすることでその動作をテストする必要があります。
そのため、クラスが別のクラスをサポートするためだけに存在し、概念的にそれが実際に独自のドメインを持たないと見なす場合、インターフェースをスキップしても構いません。しかし、クラスが十分に重要であり、独自のドメインを持つために十分に成長したと考える場合は、先に進み、インターフェイスを提供します。
編集:
頻繁に(この回答に含まれます)、「ドメイン」、「依存性」(「インジェクション」と頻繁に結合される)など、プログラムを開始するときに意味のないものを読みます(確かにそうではありませんでした)私にとっては何を意味する)。ドメインについては、次のように聞こえます。
支配権または権威が行使される領域。主権または連邦などの所有物。比fig的にも使用されます。[WordNet sense 2] [1913 Webster]
私の例の観点から-を考えてみましょうIgnitionSwitch
。ミートスペースカーでは、イグニッションスイッチは次の役割を果たします。
これらのプロパティは、のドメインを構成します。IgnitionSwitch
つまり、それが知っていて責任があるものです。
にIgnitionSwitch
は責任がありませんGasPedal
。イグニッションスイッチは、あらゆる点でアクセルペダルを完全に無視します。両者は互いに完全に独立して動作します(ただし、車は両方がなければまったく価値がありません!)。
最初に述べたように、それはあなたのデザインに依存します。IgnitionSwitch
On(True
)とOff(False
)の2つの値を持つを設計できます。または、提供されたキーと他の多くのアクションを認証するように設計できます。開発者であることは、砂地のどこに線を引くかを決めるのが難しい部分です。正直なところ、ほとんどの場合、完全に相対的です。しかし、砂の中のこれらの行は重要です-それがあなたのAPIがある場所であり、したがってあなたのインターフェースがあるべき場所です。
MSDNから:
インターフェイスは、特定の機能を提供するために、アプリケーションが多くの場合無関係なオブジェクトタイプを必要とする状況により適しています。
複数のインターフェイスを実装できる単一の実装を定義できるため、インターフェイスは基本クラスよりも柔軟です。
インターフェイスは、基本クラスから実装を継承する必要がない場合に適しています。
インターフェイスは、クラス継承を使用できない場合に役立ちます。たとえば、構造体はクラスから継承できませんが、インターフェイスを実装できます。
一般に、単一クラスの場合、インターフェースを実装する必要はありませんが、プロジェクトの将来を考慮すると、クラスの必要な動作を正式に定義することが役立つ場合があります。
質問に答えるには、それだけではありません。
インターフェイスの重要な側面の1つは、意図です。
インターフェースは「データを含まないが動作を公開する抽象型」です- インターフェース(コンピューティング)したがって、これがクラスがサポートする動作または一連の動作である場合、インターフェースはおそらく正しいパターンです。ただし、動作がクラスによって具体化される概念に固有の場合、インターフェイスはまったく必要ありません。
最初に尋ねる質問は、あなたが表現しようとしているものやプロセスの性質は何かということです。次に、その性質を特定の方法で実装する実際的な理由を続けます。
あなたがこの質問をしたので、複数の実装を隠すインターフェースを持つことの興味をすでに見たと思います。これは、依存関係の反転の原理によって明らかにすることができます。
ただし、インターフェイスを使用する必要があるかどうかは、その実装の数に依存しません。インターフェイスの実際の役割は、実装方法ではなく、提供するサービスを指定する契約を定義することです。
契約が定義されると、2つ以上のチームが独立して作業できます。モジュールAで作業しており、モジュールBに依存しているとします。Bにインターフェースを作成するという事実は、Bの実装を心配せずに作業を続行できるようにします。したがって、分散プログラミングが可能になります。
モジュールBにはインターフェースの実装が1つしかありませんが、インターフェースは依然として必要です。
結論として、インターフェイスは実装の詳細をユーザーから隠します。インターフェースへのプログラミングは、契約を定義し、より多くのモジュール式ソフトウェアを作成し、単体テストを促進し、開発速度を加速する必要があるため、より多くのドキュメントを作成するのに役立ちます。
ここのすべての答えは非常に良いです。実際、ほとんどの場合、別のインターフェイスを実装する必要はありません。しかし、あなたはケースがあるかもしれとにかくそれをやってみたいです。ここに私がそれを行ういくつかのケースがあります:
このクラスは
、サードパーティのコードをつなぐアダプタークラスで頻繁に公開したくない別のインターフェイスを実装します。
interface NameChangeListener { // Implemented by a lot of people
void nameChanged(String name);
}
interface NameChangeCount { // Only implemented by my class
int getCount();
}
class NameChangeCounter implements NameChangeListener, NameChangeCount {
...
}
class SomeUserInterface {
private NameChangeCount currentCount; // Will never know that you can change the counter
}
このクラスは、
主に外部ライブラリとやり取りするときにトラフを漏らさない特定の技術を使用します。実装が1つしかない場合でも、インターフェイスを使用して、外部ライブラリとの不要な結合を導入しないようにします。
interface SomeRepository { // Guarantee that the external library details won't leak trough
...
}
class OracleSomeRepository implements SomeRepository {
... // Oracle prefix allow us to quickly know what is going on in this class
}
クロスレイヤー通信
1つのUIクラスのみがドメインクラスの1つを実装する場合でも、それらのレイヤー間の分離を改善し、最も重要なこととして、循環依存を回避します。
package project.domain;
interface UserRequestSource {
public UserRequest getLastRequest();
}
class UserBehaviorAnalyser {
private UserRequestSource requestSource;
}
package project.ui;
class OrderCompleteDialog extends SomeUIClass implements project.domain.UserRequestSource {
// UI concern, no need for my domain object to know about this method.
public void displayLabelInErrorMode();
// They most certainly need to know about *that* though
public UserRequest getLastRequest();
}
メソッドのサブセットのみがほとんどのオブジェクトで使用可能になります
ほとんどの場合、具体的なクラスに何らかの構成メソッドがある場合に発生します
interface Sender {
void sendMessage(Message message)
}
class PacketSender implements Sender {
void sendMessage(Message message);
void setPacketSize(int sizeInByte);
}
class Throttler { // This class need to have full access to the object
private PacketSender sender;
public useLowNetworkUsageMode() {
sender.setPacketSize(LOW_PACKET_SIZE);
sender.sendMessage(new NotifyLowNetworkUsageMessage());
... // Other details
}
}
class MailOrder { // Not this one though
private Sender sender;
}
結局、私はプライベートフィールドを使用するのと同じ理由でインターフェイスを使用します。他のオブジェクトはアクセスしてはならないものにアクセスするべきではありません。そのようなケースがある場合は、たとえ1つのクラスだけで実装されていても、インターフェースを導入します。
インターフェースは本当に重要ですが、持っているインターフェースの数を制御するようにしてください。
ほぼすべてのインターフェイスを作成する道を進んでいると、「切り刻まれたスパゲッティ」コードになりやすくなります。私は、この件に関して非常に賢明な言葉を投稿したアエンデ・ラヒエンのより大きな知恵に従います。
http://ayende.com/blog/153889/limit-your-abstractions-analyzing-a-ddd-application
これはシリーズ全体の彼の最初の投稿なので、読み続けてください!
この場合にインターフェースを導入したい理由の1つは、依存関係反転の原則に従うことです。つまり、クラスを使用するモジュールは、具体的な実装に依存するのではなく、クラスの抽象化(つまり、インターフェイス)に依存します。高レベルのコンポーネントを低レベルのコンポーネントから分離します。
クラスのインターフェイスを常に定義する必要はありません。
値オブジェクトのような単純なオブジェクトには、複数の実装はありません。m笑する必要もありません。実装は単独でテストでき、それらに依存する他のクラスをテストする場合、実際の値オブジェクトを使用できます。
インターフェイスの作成にはコストがかかることに注意してください。実装に合わせて更新する必要があり、追加のファイルが必要です。また、一部のIDEでは、インターフェイスではなく実装の拡大に問題があります。
したがって、実装からの抽象化が必要な場合は、より高いレベルのクラスに対してのみインターフェースを定義します。
クラスを使用すると、無料でインターフェースを取得できることに注意してください。実装に加えて、クラスはパブリックメソッドのセットからインターフェイスを定義します。そのインターフェイスは、すべての派生クラスによって実装されます。厳密に言えばインターフェイスではありませんが、まったく同じ方法で使用できます。したがって、クラスの名前で既に存在するインターフェイスを再作成する必要はないと思います。