博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
方法该返回接口还是具体类,以及面向接口编程
阅读量:4984 次
发布时间:2019-06-12

本文共 2216 字,大约阅读时间需要 7 分钟。

这两天突然闲得蛋疼,逛了一下CSDN,发现了,于是引发了一场不大不小的关于方法应该返回接口or具体类,以及面向接口编程的讨论。

方法的返回类型应该更抽象还是更具体,没有确切的答案,唯一正确的答案是:It depends。要时情况而定。

帖子里很多牛人说,像String、List<T>这样的类型,返回具体类没有什么,特别是例子里的方法,返回IList<T>就显得很白痴。我同意String这种跟基元类型差不多的类型完全可以返回具体类,但对于List<T>和IList<T>,就完全不敢苟同了。

因为,在设计API时(这里主要讨论方法),你需要控制留给用户“权限”。假如你的方法只希望返回一个只读的可枚举的集合类型,那么就应该返回IEnumerable<T>,这样用户就不能进行修改、添加、排序和转换。而如果你希望给用户一个不可转换的列表,就应该返回IList<T>,这样用户就不能调用ConvertAll方法。如果你希望留给用户的操作完全和List<T>一致,这时返回具体的List<T>就是最适当的。List<T>虽然十分常用,但它与IList<T>还是有区别的,不能简单地将它跟String这种类型划等号。为了验证我的想法,我在Stack Overflow上搜到了,偶像的回答跟我的想法不谋而合。当然,这也应该是绝大多数同学的想法。

我的另一个观点是:在设计API时,让方法返回接口类型是面向接口编程的一部分。但很多人指出我对面向接口编程的理解有误。

这里首先要说明一下什么是面向接口编程。据我所知,这个原则最早是在《设计模式》里提出来的,即Program to an interface, not an implementation(针对接口编程,而不是针对实现)。意思是指:

  • 我们应该根据抽象类中定义的接口来操纵对象,用户只需要知道定义这些接口的抽象类,而不必关心使用的是什么具体类型。
  • 在声明变量时,不将变量声明为某个特定的具体类,而是让它遵从抽象类所定义的接口,即将变量声明为抽象类。
  • 在实例化的时候,使用创建型模式。创建型模式可以确保系统是针对接口的方式书写,而不是针对实现的方式书写。

可以看到,这里的接口,与IList<T>这种接口不是一个概念,所以我用斜体字来表示Program to an interface里的接口

这里的接口是指,抽象类对外声明的契约,比如各种公共方法等。用户代码应该只跟这些契约有关,而不应该跟这些契约的实现有关。比如,抽象类A中定义了一个方法M,用户在声明变量时应该这样:A a = …,在调用时是这样:a.M()。用户代码所关心的问题就是调用A定义的M,而不用理会究竟是子类C1的M,还是C2的M,这样,用户代码就跟具体的实现解耦了。

然而我们必须要知道,《设计模式》这本书是针对C++这门语言的,而C++没有C#中的接口这个概念。在C#和Java里,接口是和抽象类差不多同一层次的抽象,它仅仅提供契约,而不提供任何默认实现。所以对于接口IA,声明时和调用时采用这样的代码:IA a = …; a.M(); 也同样是针对接口编程。这不应该有任何异议吧?

现在回到我之前的观点:将方法的返回类型设计为接口,是否是针对接口编程的一部分?我们来看代码

public interface IBar{    void N();}public class Bar : IBar{    public void N(){}}public class Foo{    public static IBar M()    {        return new Bar();    }}

在用户代码中,我们可以这样声明一个IBar:

IBar bar = Foo.M();

我在CSDN的帖子里指出,如果Foo的M方法返回一个Bar,那么用户在声明变量的时候仍然使用IBar bar = Foo.M()是没有任何意义的,因为已经明确知道M返回的是具体类型了。现在想想其实也是有意义的。至少在API改变的时候,如以后将M的返回值改成IBar或其他实现了IBar的类时,客户代码不需要修改。这也是针对接口编程的一个意义所在。

那么我们在设计API的时候让M返回IBar而不是Bar,这样做的意义何在呢?我们可以强制用户在声明变量的时候使用IBar bar而不是Bar bar,从而避免由于M的实现修改后(比如返回了另一个实现了IBar的类)造成客户端代码无法编译的情况。因此我说,返回接口,也是针对接口编程的一部分。事实上上面所描述的《设计模式》这本书中关于针对接口编程的第三点——实例化变量的时候使用创建型模式,就是这个意思。

不知道我说明白了没有,列位看明白了没有。对于面向接口编程,我也搜到了很多帖子,希望对大家有帮助:

另外我还在Stack Overflow上单独开了一贴,询问返回接口是否能与针对接编程挂钩,也有人说我对“针对接口编程”有误解,不过我觉得他也没明白我的意思,我当然知道两种“接口”的不同啦:)

转载于:https://www.cnblogs.com/kirinboy/archive/2012/03/08/should-method-return-interface-or-concrete-class-and-programming-to-an-interface.html

你可能感兴趣的文章
静态VLAN和动态VLAN
查看>>
C语言中如何对串口进行操作
查看>>
纯CSS制作各种图形(多图预警)
查看>>
BZOJ 4517: [Sdoi2016]排列计数(组合数学)
查看>>
使用Jquery,formData,Express,multer中间件实现文件上传
查看>>
学习总结:机器学习(二)
查看>>
HDU 1097[A hard puzzle]循环节
查看>>
百度seo
查看>>
【网络设备】某防火墙基于IP地址的目的地址转换
查看>>
算法: 最长回文子串 二层动态规划
查看>>
硬件综合实习——51单片机四则运算带括号计算器
查看>>
cmake写完了,下一步开始清除编译错误
查看>>
Linux中mod相关的命令 内核模块化 mod相关命令都是用来动态加载内核模块/驱动程序模块...
查看>>
行列转换总结
查看>>
Android 学习笔记之ExpandableListView UI的简单用法
查看>>
12306-车
查看>>
ADO.NET+Access: 2,至少一个参数没有被指定值
查看>>
Day 04
查看>>
centos7更改网卡名称
查看>>
ckeditor_学习(1) 基本使用
查看>>