我有一个朋友刚刚开始使用Java进行.NET开发很长时间了,在查看了他的一些代码后,我注意到他经常做以下事情:
IDictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();
他将字典声明为接口而不是类。通常我会做以下事情:
Dictionary<string, MyClass> dictionary = new Dictionary<string, MyClass>();
我只在需要时使用IDictionary接口(例如,将字典传递给接受IDictionary接口的方法)。
我的问题是:他的做事方式有什么好处吗?这是Java中的常见做法吗?
答案 0 :(得分:59)
如果IDictionary是一个比Dictionary更“通用”的类型,那么在声明变量时使用更通用的类型是有意义的。这样您就不必太在意分配给变量的实现类,并且您可以在将来轻松更改类型,而无需更改大量以下代码。例如,在Java中,通常认为做得更好
List<Integer> intList=new LinkedList<Integer>();
比做
LinkedList<Integer> intList=new LinkedList<Integer>();
这样我确定所有后续代码都将列表视为List而不是LinkedList,这样可以在将来轻松切换LinkedList for Vector或任何其他实现List的类。我会说这对Java来说很常见,而且编程也很好。
答案 1 :(得分:27)
这种做法不仅限于Java。
当你想要从你正在使用的类中解除对象的实例时,它也经常在.NET中使用。如果使用Interface而不是Class,则可以在需要时更改支持类型,而不会破坏其余代码。
您还会看到这种做法在使用工厂模式处理IoC容器和实例化时会大量使用。
答案 2 :(得分:14)
你的朋友正在遵循这个非常有用的原则:
“从实施细节中抽象出来”
答案 3 :(得分:8)
对于已经是实现细节的局部变量和私有字段,最好使用具体类型而不是声明的接口,因为具体类提供了性能提升(直接调度比虚拟/接口调度更快)。如果您没有在本地实现细节中不必要地转换为接口类型,JIT也将能够更容易地内联方法。如果从返回接口的方法返回具体类型的实例,则转换是自动的。
答案 4 :(得分:7)
你应该总是尝试编程到界面而不是具体的类。
使用Java或任何其他面向对象的编程语言。
在.NET中,通常使用I
表示这是您正在使用的界面。我认为这更常见,因为在C#中他们没有implements
和extends
来引用类和接口继承。
我认为乳清会输入
class MyClass:ISome,Other,IMore
{
}
您可以告诉ISome
IMore
是接口而Other
是一个类
在Java中不需要这样的东西
class MyClass extends Other implements Some, More {
}
这个概念仍然适用,你应该尝试编写接口代码。
答案 5 :(得分:5)
据我所知,Java开发人员倾向于比.NET开发人员更频繁地使用抽象(和设计模式)。这似乎是另一个例子:为什么在他基本上只使用接口成员时声明具体类?
答案 6 :(得分:4)
大多数情况下,您会看到当成员公开外部代码时使用的接口类型(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; } } }
不改变Widget的界面,并且必须更改已经使用它的其他代码。
答案 7 :(得分:3)
在所描述的情况下,几乎每个Java开发人员都会使用该接口来声明变量。 Java集合的使用方式可能是最好的例子之一:
Map map = new HashMap();
List list = new ArrayList();
猜猜它只是在很多情况下完成松耦合。
答案 8 :(得分:3)
Java Collections包含大量实现。因此,我更容易使用
List<String> myList = new ArrayList<String>();
然后将来当我意识到我需要“myList”是线程安全的,只需将这一行更改为
List<String> myList = new Vector<String>();
并且不更改其他代码行。这包括getter / setter。例如,如果你看一下Map的实现数量,你可以想象为什么这可能是一个好习惯。在其他语言中,只有几个实现的东西(对不起,不是一个大的.NET人),但在Objective-C中,实际上只有NSDictionary和NSMutableDictionary。所以,它没有多大意义。
编辑:
无法点击我的一个关键点(只是使用getter / setter提到它)。
以上允许您拥有:
public void setMyList(List<String> myList) {
this.myList = myList;
}
使用此调用的客户端无需担心底层实现。使用符合List接口的任何对象。
答案 9 :(得分:3)
来自Java世界,我同意“钻石界面程序”的口号是钻进你的。通过编程到接口而不是实现,您可以使您的方法更具可扩展性,以满足未来的需求。
答案 10 :(得分:1)
我发现对于局部变量,无论你使用的是接口还是具体的类,它通常都没什么关系。
与类成员或方法签名不同,如果您决定更改类型,则很少有重构工作,变量在其使用站点之外也不可见。实际上,当您使用var
声明本地时,您没有获得接口类型,而是获取类类型(除非您明确地转换为接口)。
但是,在声明方法,类成员或接口时,我认为预先使用接口类型会让您感到非常头疼,而不是将公共API耦合到特定的类类型。
答案 11 :(得分:1)
使用接口意味着以下代码中的“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(...);
答案 12 :(得分:1)
我遇到过与Java开发人员相同的情况。他以相同的方式将集合AND对象实例化到它们的接口。 例如,
IAccount account = new Account();
属性始终作为接口获取/设置。这会导致序列化问题,这可以很好地解释here
答案 13 :(得分:0)
IDictionary
是一个接口,Dictionary
是一个类。
Dictionary
实现IDictionary
。
这意味着可以用Dictionary
实例引用IDictionary
实例,并通过Dictionary
实例调用大多数IDictionary
方法和属性。
强烈建议您尽可能多地使用接口,因为接口抽象了应用程序的模块和程序集,允许多态,这在很多情况和情况下非常普遍和有用,并且允许在不接触的情况下将一个模块替换为另一个模块其他模块。
假设当前程序员编写:
IDictionary<string> dictionary = new Dictionary<string>();
现在dictionary
调用Dictionary<string>
的方法和属性。
将来数据库的规模将不断扩大,我们发现Dictionary<string>
太慢了,因此我们想用Dictionary<string>
来代替RedBlackTree<string>
,这是更快的方法。 / p>
因此,所有需要做的就是将上述说明替换为:
IDictionary<string> dictionary = new RedBlackTree<string>();
当然,如果RedBlackTree
实现了IDictionary
,则该应用程序的所有代码都将成功编译,并且您拥有该应用程序的新版本,该应用程序现在的性能比以前的版本更快,更高效。
没有接口,这种替换将更加困难,并且将要求程序员和开发人员更改更多可能导致错误的代码。