私はJavaで長年開発してきたばかりの.NET開発を始めたばかりの友人がいます。彼のコードを見て、次のことを頻繁に行っていることに気付きます。
IDictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();
彼は辞書をクラスではなくインターフェースとして宣言しています。通常、次のことを行います。
Dictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();
IDictionaryインターフェイスは、必要な場合にのみ使用します(たとえば、IDictionaryインターフェイスを受け入れるメソッドに辞書を渡す場合など)。
私の質問は、彼のやり方にメリットはありますか?これはJavaの一般的な習慣ですか?
IDictionaryがDictionaryよりも「より一般的な」型である場合、変数を宣言する際により一般的な型を使用することは理にかなっています。そうすれば、変数に割り当てられた実装クラスをそれほど気にする必要がなく、将来多くのコードを変更することなく、簡単に型を変更できます。たとえば、Javaでは、行う方が良いと考えられることがよくあります
List<Integer> intList=new LinkedList<Integer>();
するよりも
LinkedList<Integer> intList=new LinkedList<Integer>();
そうすれば、以下のすべてのコードがリストをLinkedListではなくListとして処理し、将来的にVectorのLinkedListまたはListを実装する他のクラスを簡単に切り替えることができると確信しています。これは、Javaと一般的な優れたプログラミングに共通です。
この方法は、Javaだけに限定されません。
使用しているクラスからオブジェクトのインスタンスを切り離したい場合にも、.NETでよく使用されます。クラスではなくインターフェイスを使用する場合、コードの残りの部分を壊すことなく、必要に応じてバッキングタイプを変更できます。
このプラクティスは、IoCコンテナーの処理とFactoryパターンを使用したインスタンス化で頻繁に使用されることもわかります。
あなたの友人は非常に有用な原則に従っています:
「実装の詳細から抜け出す」
既に実装の詳細であるローカル変数とプライベートフィールドの場合、具体的なクラスがパフォーマンスを向上させるため、宣言のインターフェイスよりも具体的な型を使用する方が適切です(直接ディスパッチは仮想/インターフェイスディスパッチよりも高速です)。 JITは、ローカル実装の詳細でインターフェイスタイプに不必要にキャストしない場合、メソッドをより簡単にインライン化することもできます。インターフェイスを返すメソッドから具象型のインスタンスが返される場合、キャストは自動的に行われます。
具象クラスではなく、常にインターフェイスにプログラムするようにしてください。
Javaまたはその他のオブジェクト指向プログラミング言語。
.NETの世界では、I
を使用して、使用しているインターフェイスであることを示すのが一般的です。 C#では、クラスとインターフェイスの継承を参照するimplements
とextends
がないため、これがより一般的だと思います。
ホエイはタイプすると思います
class MyClass:ISome,Other,IMore
{
}
そして、ISome
とIMore
はインターフェースであり、Other
はクラスであると言うことができます。
Javaでは、そのようなことは必要ありません
class MyClass extends Other implements Some, More {
}
概念はまだ適用されます、インターフェイスへのコード化を試みる必要があります。
私が見た限りでは、Java開発者は.NET開発者よりも抽象化(および設計パターン)を使用する傾向があります。これは別の例のようです。本質的には、インターフェイスメンバのみを操作しているのですか?
ほとんどの場合、メンバーが外部コードに公開されているときに使用されるインターフェイスタイプ(IDictionary)が表示されます。これは、アセンブリの外部またはクラスの外部にあります。通常、ほとんどの開発者は、インターフェイスタイプを使用してカプセル化されたプロパティを公開する一方で、クラス定義に対して内部的に具象タイプを使用します。このようにして、具象型の機能を活用できますが、具象型を変更しても、宣言クラスのインターフェースを変更する必要はありません。
public class Widget { private Dictionary <string、string> map = new Dictionary <string、string>(); public IDictionary <string、string> Map { get {return map; } } }
後になります:
class SpecialMap <TKey、TValue>:IDictionary <TKey、TValue> {...} public class Widget { private SpecialMap <string、string> map = new SpecialMap <string、string>(); public IDictionary <string、string> Map { get {return map ; } } }
ウィジェットのインターフェースを変更したり、すでに使用している他のコードを変更したりする必要はありません。
Javaコレクションには、多数の実装が含まれています。したがって、私が利用するのははるかに簡単です
List<String> myList = new ArrayList<String>();
その後、将来、この単一行を単に変更するために「myList」がスレッドセーフである必要があることに気付いたとき
List<String> myList = new Vector<String>();
そして、他のコード行を変更しません。これにはゲッター/セッターも含まれます。たとえば、Mapの実装の数を見ると、なぜこれが良い習慣になるのか想像できます。他の言語では、何かの実装は数個しかありません(ごめんなさい、大きな.NETの男ではありません)が、Objective-Cでは実際にはNSDictionaryとNSMutableDictionaryしかありません。したがって、それはあまり意味がありません。
編集:
キーポイントの1つにヒットしませんでした(ゲッター/セッターで暗示されました)。
上記により、以下が可能になります。
public void setMyList(List<String> myList) {
this.myList = myList;
}
そして、この呼び出しを使用するクライアントは、基礎となる実装について心配する必要はありません。 Listインターフェイスに準拠しているオブジェクトを使用します。
説明した状況では、ほとんどすべてのJava開発者がインターフェイスを使用して変数を宣言します。Javaコレクションの使用方法は、おそらく最良の例の1つです。
Map map = new HashMap();
List list = new ArrayList();
多くの状況で疎結合を達成するだけだと思います。
Javaの世界では、「インターフェースへのプログラム」というマントラがあなたに掘り下げられていることに同意します。 。
Java開発者。同じ方法でコレクションとオブジェクトをインターフェイスにインスタンス化します。たとえば、
IAccount account = new Account();
プロパティは常にインターフェイスとして取得/設定されます。これにより、シリアル化で問題が発生します。これは非常によく説明されています ここ
ローカル変数の場合、インターフェイスを使用するのか具体的なクラスを使用するのかは一般的にそれほど重要ではないことがわかりました。
クラスメンバやメソッドシグネチャとは異なり、型を変更することを決めた場合、リファクタリングの労力はほとんどありません。また、変数はその使用サイトの外部に表示されません。実際、var
を使用してローカルを宣言すると、インターフェイスタイプではなくクラスタイプを取得します(明示的にインターフェイスにキャストしない限り)。
ただし、メソッド、クラスメンバー、またはインターフェイスを宣言する場合、パブリックAPIを特定のクラスタイプに結合するのではなく、インターフェイスタイプを事前に使用することで頭痛の種がかなり減ると思います。
インターフェイスを使用すると、次のコードの「辞書」はIDictionaryの実装になります。
Dictionary1 dictionary = new Dictionary1();
dictionary.operation1(); // if operation1 is implemented only in Dictionary1() this will fail for every other implementation
オブジェクトの構造を非表示にすると、最もよく見られます。
IDictionary dictionary = DictionaryFactory.getDictionary(...);
IDictionary
はインターフェースであり、Dictionary
はクラスです。
Dictionary
はIDictionary
を実装します。
つまり、Dictionary
インスタンスを使用してIDictionary
インスタンスを参照し、Dictionary
インスタンスを介してほとんどのIDictionary
メソッドとプロパティを呼び出すことができます。
インターフェイスはアプリケーションのモジュールとアセンブリを抽象化し、多くの状況とケースで非常に一般的で便利な多態性を可能にし、他のモジュールに触れずにモジュールを別のものに置き換えることができるため、できるだけ多くのインターフェイスを使用することをお勧めします。
現在、プログラマが次のように書いているとします:
IDictionary<string> dictionary = new Dictionary<string>();
そして今、dictionary
は_Dictionary<string>
_のメソッドとプロパティを呼び出します。
将来、データベースのサイズが大きくなり、_Dictionary<string>
_が遅すぎることがわかったため、_Dictionary<string>
_を_RedBlackTree<string>
_に置き換えたいと思います。
したがって、実行する必要があるのは、上記の指示を次のように置き換えることだけです。
IDictionary<string> dictionary = new RedBlackTree<string>();
もちろん、RedBlackTree
がIDictionary
を実装する場合、アプリケーションのすべてのコードが正常にコンパイルされ、アプリケーションの新しいバージョンがあります。アプリケーションは以前のバージョンよりも高速で効率的になります。
インターフェイスがなければ、この置換は実行が難しくなり、プログラマや開発者はバグの可能性があるコードをさらに変更する必要があります。