风格上的差异: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#中他们没有implements
并extends
到引用类与接口的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接口的对象。