风格上的差异:IDictionary vs Dictionary

我有一个朋友在Java开发了很久之后才开始进行.NET开发,在看了他的一些代码之后,我发现他经常这样做:

IDictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>(); 

他将字典声明为接口而不是类。 通常我会做以下几点:

 Dictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>(); 

在需要的时候,我只会使用IDictionary接口(比如将字典传递给一个接受IDictionary接口的方法)。

我的问题是:他的做事方式有什么好处吗? 这在Java中是一种常见的做法吗?

如果IDictionary是比Dictionary更“通用”的types,那么在声明variables时使用更通用的types是有意义的。 这样,您不必关心分配给variables的实现类,而且您可以在将来轻松更改types,而无需更改以下大量代码。 例如,在Java中,通常认为这样做更好

 List<Integer> intList=new LinkedList<Integer>(); 

比它要做的还要多

 LinkedList<Integer> intList=new LinkedList<Integer>(); 

这样,我确信下面的所有代码将列表视为一个列表而不是一个LinkedList,使得将来可以轻松地将Vector的LinkedList或任何其他实现List的类转换出来。 我会说这对于Java和一般的良好编程是很普遍的。

这种做法不仅限于Java。

当你想从你正在使用的类中取消对象的实例时,它通常也用在.NET中。 如果使用接口而不是类,则可以根据需要更改后备types,而不会破坏其他代码。

你也会看到这个练习在处理IoC容器和使用Factory模式的实例方面大量使用。

你的朋友是遵循非常有用的原则:

“从实施细节中抽象出你自己”

你应该总是试图编程到接口而不是具体的类。

在Java或任何其他面向对象的编程语言。

在.NET世界中,使用I来表示这是一个你正在使用的接口。 我认为这是比较常见的,因为在C#中他们没有implementsextends到引用类与接口的inheritance。

我想乳清会打字

  class MyClass:ISome,Other,IMore { } 

你可以告诉ISome一个IMore是接口而Other是一个类

在Java中,不需要这样的事情

  class MyClass extends Other implements Some, More { } 

这个概念仍然适用,你应该尝试编码到接口。

据我所见,Java开发人员比.NET开发人员更倾向于使用抽象(和devise模式)。 这似乎是另一个例子:为什么要声明具体的类时,他基本上只会与接口成员一起工作?

大多数情况下,当成员暴露给外部代码时,您会看到使用的接口types(IDictionary),无论是在程序集之外还是在类之外。 通常,大多数开发人员在使用接口types公开封装属性的同时,将内部具体types用于类定义。 通过这种方式,他们可以利用具体types的function,但是如果他们改变具体types,声明类的接口就不需要改变。

公共类Widget
 {
     private Dictionary <string,string> map = new Dictionary <string,string>();
    公共IDictionary <string,string>地图
     {
        得到{return map;  }
     }
 }

以后可以成为:


 class SpecialMap <TKey,TValue>:IDictionary <TKey,TValue> {...}

公共类Widget
 {
     private SpecialMap <string,string> map = new SpecialMap <string,string>();
    公共IDictionary <string,string>地图
     {
        得到{return map;  }
     }
 }

而无需更改Widget的界面,并且不得不更改已经使用它的其他代码。

对于已经是实现细节的局部variables和私有字段,最好使用具体的types而不是声明的接口,因为具体类提供了性能提升(直接调度比虚拟/接口调度更快)。 如果您不需要在本地实现细节中转换为接口types,JIT也将能够更轻松地内联方法。 如果从返回接口的方法返回具体types的实例,则该types是自动的。

在描述的情况下,几乎每个Java开发人员都会使用该接口来声明variables。 Java集合的使用方式可能是最好的例子之一:

 Map map = new HashMap(); List list = new ArrayList(); 

猜测它在很多情况下只是完成了松耦合。

来自Java世界,我同意“程序到界面”的口头禅是钻进你的。 通过编程到一个接口,而不是一个实现,你可以使你的方法更多的扩展到未来的需求。

我发现对于局部variables来说,使用接口或具体类通常并不重要。

与类成员或方法签名不同的是,如果决定更改types,则重构的工作量非常小,在其使用位置外部也不可见variables。 实际上,当你使用var来声明locals的时候,你并没有得到接口types,而是获得了类的types(除非你明确的转向接口)。

但是,在声明方法,类成员或接口的时候,我认为这样可以节省很多使用接口types的麻烦,而不是将公共API耦合到特定的类types。

使用接口意味着下面的代码中的“dictionary”可能是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(...); 

我遇到了与Java开发人员相同的情况。 他以同样的方式将集合和对象实例化到它们的接口。 例如,

 IAccount account = new Account(); 

属性总是被设置为接口。 这会导致序列化问题, 在这里解释得非常好

Java集合包含了大量的实现。 因此,我更容易利用

 List<String> myList = new ArrayList<String>(); 

那么在将来,当我意识到我需要“myList”是线程安全的,只需将此单行更改为

 List<String> myList = new Vector<String>(); 

并且不要更改其他代码行。 这也包括getters / setters。 例如,如果你看一下Map实现的数量,你可以想象为什么这可能是一个好的做法。 在其他语言中,只有一些东西的实现(对不起,不是一个大的.NET人),但在Objective-C中只有NSDictionary和NSMutableDictionary。 所以,这并没有太大的意义。

编辑:

无法击中我的一个关键点(刚刚提到的getter / setters)。

以上允许你有:

 public void setMyList(List<String> myList) { this.myList = myList; } 

而使用这个调用的客户端不需要担心底层的实现。 使用任何符合它们可能具有的List接口的对象。