この方法でインスタンス化されたオブジェクトを時々見ます。
ICustomer oCustomer = new Customer
当然ですが、この例ではCustomerクラスがICustomerインターフェイスを実装しています。そのようにオブジェクトをインスタンス化することの利点はありますか?私の理解から、インターフェースを実装するクラスがすでに契約に従っている場合。そのプラクティスの名前は何ですか?オブジェクトを「通常の」方法でインスタンス化することには不利な点がありますか(私は通常何をしますか)?私はすでにいくつかの既存の質問があることを知っていますが、それらは本当にメリットとデメリットを強調していません
Customer oCustomer = new Customer
非常にあなたが見落としていると思う重要な区別が1つあります。
指定したコードは、変数の宣言、オブジェクトのインスタンス化、およびそのオブジェクトを使用した変数の初期化の3つのためのものです。
インターフェイスの実装はありませんここ。 Customer
はICustomer
を実装する(または他の1つまたは2つのトリックを実行する)必要があります正常にコンパイルされますが、ここでは実際の実装は行われていません。
用語が邪魔になったので、問題の本質に焦点を当てましょう。
この種のものは インターフェイスへのプログラム の領域に入ります。変数を宣言するときは、型を指定する必要があります。その型は、変数が実行できることの種類を定義する必要があります。 oCustomer
がこのようなことしかできないのは、将来のプログラマー(またはあなた自身)と契約を結ぶことです。変数の初期化に使用するソースのこの規約に関係なく決定する必要があります。
この変数がインターフェイスのみを必要とする場合、そのタイプとしてインターフェイスを使用することで、実行できる(およびできない)ことが明確に定義されます。この変数reallyを完全なCustomer
にする必要がある場合は、really必要です。もしそうなら、それからCustomer
にします。これは、変数がその型である必要があることを将来のプログラマー(またはあなた)に明確に伝えます。変数がイニシャライザの単なるプレースホルダである場合。タイプが本当に重要でない場合は、var
を使用します。
インターフェースへのプログラミングの利点は、上記のリンクで詳細に説明されていますが、結局、柔軟に対応できるようになります。ソフトウェアは必然的に変化します。インターフェースにコーディングすることにより、Customer
が変更された場合、または代わりにoCustomer
をVipCustomer
で初期化する必要がある場合に、このコードが気付かずに気付かないようにすることができます。
この種のことは、変数宣言だけでなく、プログラミング全体の多くの場所に当てはまります。
あなたのコードがこのように単純に見える限り
void ExampleFunc()
{
ICustomer oCustomer = new Customer();
oCustomer.Method1OfICustomer();
oCustomer.Method2OfICustomer();
// ...
}
no意味の違いがあります-「ICustomer」を「Customer」で交換でき、動作は同じままです。ただし、この例では、すでにいくつかの記録目的を達成できます。最初の行は、次のような呼び出しの追加を避けるために、プログラマーの設計上の決定を表す場合があります。
oCustomer.MethodNotPartOfICustomer();
将来、しかし実際には、この種の呼び出しが後で必要になったときに、「新しい」行を変更したり、不足しているメソッドをICustomer
laterに追加したりすることもできます。
実際、用語が意味するように、インターフェイスの使用は、いくつかの実際の「インターフェース」、つまり関数、クラス、コンポーネント間のインターフェースがある場合に、より理にかなっています。たとえば、関数
void ExampleFunc(ICustomer oCustomer)
{
oCustomer.Method1OfICustomer();
oCustomer.Method2OfICustomer();
// ...
}
おそらくより一般的です
void ExampleFunc(Customer oCustomer)
{
oCustomer.Method1OfICustomer();
oCustomer.Method2OfICustomer();
// ...
}
ICustomer
だけでなく、Customer
インターフェースを実装するすべてのクラスで機能するためです。たとえば、テストの目的で、「CustomerMock」を関数に渡すことを考えることができます。これは、2番目のバリアントでは実行できないものです。
作成方法は決して良くも悪くもありません。
それについて考えてみましょう:
オブジェクトを設定するか、オブジェクトを取得します(つまり、オブジェクトをコンストラクター、メソッドに渡すか、メソッドから返します)。
どちらの場合も設定と取得のためのインターフェイスを提供している場合ポリモーフィズム、抽象化、およびカプセル化を利用できるため、任意の方法でオブジェクトを新規作成できます。全体の全体のポイントprogram to interface not to implementation
原則、これはあなたの中心的な質問です。
C#ではそれほど大きな違いはありません。
違いは、作成した参照で呼び出すことができる内容にあります。
このコード
_ICustomer customer = new Customer();
_
new Customer();
呼び出しによって作成されたインスタンスへのcustomer
という名前のインターフェース参照を作成します。
このコード
_Customer customer = new Customer();
_
new Customer();
呼び出しによって作成されたインスタンスへのオブジェクト参照名customer
を作成します。
どちらの参照もCustomer
クラスのインスタンスを指していますが、それらは異なります。インターフェース参照は、ICustomer
クラスが他に何を使用可能にするかに関係なく、Customer
インターフェースで定義されているもののみを提供します。オブジェクト参照を使用すると、Customer
クラスが提供するすべてのものを使用できます。
インターフェース参照を使用する場合、クラスが提供する「追加の」ものを使用するには、それを実装クラスにキャストバックする必要があります。あなたが本当にやることしないやりたいこと。結局のところ、インターフェース参照が渡されたときに、それが参照するオブジェクトのインスタンス化にどのクラスが使用されたかはわかりません。このルールには例外が1つだけあります。自分でインターフェイス参照を作成したばかりで、インスタンスの構築/構築/初期化中にのみ必要であるため、インターフェイスに含めたくないメソッドを使用する必要がある場合。そして、これもまれなケースです。
C#については、それで終わりです。
開発者がインスタンス化されたオブジェクトの明示的なライフタイム管理(つまり、各オブジェクトを明示的に解放する必要がある)を担当する言語の場合、インスタンス化されたオブジェクトを参照する2つの方法には大きな違いがあります。
たとえばDelphiでは、インターフェース化されたオブジェクト(インターフェースを実装するクラスのインスタンス)はデフォルトで参照カウントされ、参照カウントがゼロになると「自動的に」解放されます。参照カウントは、コンパイラーによってコードに自動的に追加される__AddRef
_および__Release
_呼び出しによって増分および減分されます。
Delphiでは、インターフェイスクラスのインスタンスへのオブジェクト参照を作成したくありません。これにより、コンパイラーは、インスタンス化時に参照カウントを1にインクリメントする__AddRef
_呼び出しを挿入しません。これは、参照カウントが少なすぎることを意味します。つまり、インスタンスはすぐに解放されます。
そのため、Delphiなどの言語に関するアドバイスは、インターフェイスとオブジェクト参照を混在させないことと常にインターフェイスを使用することですインターフェースクラスの参照。