你是否为类编写接口?我乞求你停下!
目录
介绍
我们为什么需要接口?
那么我们为什么不能使用它们呢?
真正的问题——业务逻辑
但我需要接口......
所以我该怎么做?
介绍
关于如何编写好代码有很多指南。许多组织实现静态代码分析以验证和提高代码质量。开发人员越来越意识到干净的代码是什么,SOLID,设计模式,GRASP ......如此多的指令如何编码,所以我们开始忘记这些是什么。
我们为什么需要接口?
接口的第一个用例是描述实现它的对象的行为。如果一个类Dog实现了接口IAnimal,我们确信它(并非总是)可以Move和Eat。当然,Dog也可以Bark或者Sit,但这是具体的实现,不同于Bird可以Fly。
接口的更复杂的用例是多态(通常与依赖注入相结合)。如果我们的代码依赖于抽象(在这种情况下是接口),那么它更灵活。我们可以使用任何我们想要的类,它实现了必要的接口。
由于接口,我们可以减少类之间的耦合。如果您的实现基于抽象(例如接口),则可以通过更改接口的实现来更改应用程序的输出而无需更改源代码。
那么我们为什么不能使用它们呢?
在我刚开始做开发的时候,我没有问很多问题,我只是做了其他更有经验的同事所做的事。但过了一段时间,我开始看到设计和实现方面的缺陷。
你有多少次见过这样的接口:
namespace Calculator
{public interface IMathCalculator{int Sum(int a, int b);int Subtract(int a, int b);}
}
通过这样的实现:
namespace Calculator
{public class MathCalculator : IMathCalculator{public int Sum(int a, int b){return a + b;}public int Subtract(int a, int b){return a - b;}}
}
甚至是这样的:
public class CalculatorConfigProvider : ICalculatorConfigProvider
{public double GetPiValue(){return 3.14;}
}public interface ICalculatorConfigProvider
{double GetPiValue();
}
好的,那么这段代码可能有什么问题呢?
在我看来,这个接口完全没有必要。接口建议可能的多个实现。在这种情况下,我们只有一个具有接口名称的实现。接口及其实现位于同一名称空间中,因此使用具体实现而不是抽象并不成问题。
更重要的是,如果使用IoC容器(例如,Ninject),则需要使用其接口注册所有类。这是另一件要记住的事情,它可能看起来像这样:
Kernel.Bind<ICalculatorConfigProvider>().To<CalculatorConfigProvider>();
Kernel.Bind<IMathCalculator>().To<MathCalculator>();
Kernel.Bind<ICalculatorResolver>().To<CalculatorResolver>();
项目越大,注册IoC 容器时添加的条目就越多。初始化类变得更大,更难维护。如果需要在两个不同的作用域中注册依赖项(例如,WebApi的请求范围和某些异步无限循环的命名作用域),则需要两次注册依赖项,这是一个灾难。
真正的问题——业务逻辑
我们谈论的是良好实践、耦合、可维护性等。但我认为真正的问题是当你处理域和业务逻辑时。这是应用程序的关键部分——有时是应用程序,甚至是整个公司的关键点和目的。这部分包含一些严肃的计算,算法或规则。
如果您向此类添加接口然后注入此接口,则您无法控制它的使用方式。特别是当您制作API或其他应用程序使用的库时。
OrderGenerator执行从Products生成Order的域逻辑的一个简单示例显示了一个想法:
public class OrderGenerator
{private readonly TaxCalculator calculator;public OrderGenerator(TaxCalculator calculator){this.calculator = calculator;}public Order Generate(IEnumerable<Product> products){var order = new Order();foreach (var product in products){var price = calculator.CalculatePrice(product);order.AddPosition(product, price);}return order;}
}
TaxCalculator注入到OrderGenerator并根据产品类型、税收价格和国家法律进行一些计算。
public class TaxCalculator
{public Price CalculatePrice(Product product){// Some domain-specific calculations for getting price of a product.}
}
通过此实现,您可以确定OrderGenerator将为给定产品正确计算价格。
现在考虑代码审查,一些资深专家建议在TaxCalculator中增加一个接口,并在OrderGenerator中注入一个ITaxCalculator。看起来很简单,这听起来是个好主意。根据SOLID您认为接口隔离,您知道接口是好的。代码看起来也不错。:)
public interface ITaxCalculator
{public Price CalculatePrice(Product product);
}
现在,OrderGenerator开始进行修改。这是什么意思?这意味着您可以实现一个新的TaxCalculator,例如,NoTaxCalculator实现ITaxCalculator和返回0价格。
问题是它是您的应用程序或者你的生态系统或者你的公司的关键部分。
现在,有人可以打破它。:)
但我需要接口......
嗯......我听说过为什么有必要在类中添加接口。这里是其中的一些:
- 在IoC 容器中注册类以将它们注入另一个类
- 单元测试中的模拟测试
- 编写干净的代码并减少类之间的耦合
但是,这是真的吗?
当IoC容器包含一个public构造函数时,它应解析一个类,该构造函数包含可由IoC 容器解析的所有参数。
如果你为测试添加一个JUST接口——不要。您的生产代码不应该只是为了满足测试而编写。您可以在测试项目中执行任何操作,但保留生产代码。:)
使用接口而不是具体实现可以减少耦合,这是事实。但是在IoC容器中注册接口会增加复杂性并降低可维护性,因此如果您在应用程序中仅使用它一次,那么我认为它的优缺点相等。
所以我该怎么做?
首先,您应该考虑满足所有业务需求。:)如果您编写有史以来最干净的代码,但不满足您的业务,那么该应用程序是无用的。
接口具有很大的价值,应该在每个应用程序中使用它们。多亏了接口,你可以使用多态,添加很多模式,比如我们的策略、工厂、命令模式等等。您可以反向依赖(在SOLID中为D),因此如果您的域使用存储库或适配器,则可以注入接口,并且实现应该在单独的层中。
但作为一切,你应该明智地使用它们。:)并非所有类都应该有自己的接口。这不是唯一的解决方案。
你是否为类编写接口?我乞求你停下!相关推荐
- 后台数据库连接以及工具类编写 [木字楠博客]
文章目录 1.项目连接数据库 1.1.引入依赖信息 1.2.编写配置文件 1.3.新增数据库配置类 1.4.代码生成器编写 2.通用枚举类编写 3.统一结果集处理器 4.其他工具类 4.1.Bean拷 ...
- 业务逻辑写在存储过程好还是后端好_后端两小时,前端一星期!你只是一个写接口的工程师吗?...
故事从一名前端工程师的呐喊开始: 我们公司做项目分配任务,一个星期的活,后端呢,两三个小时就弄完了,前端要做界面,做交互,做功能,忙活一个星期还得和设计师对 UI,和需求方对业务逻辑,最后呢,管事的还 ...
- python连接sqlserver_python 链接sqlserver 写接口实例
我是使用pymssql完成的sqlserver,首先下载符合版本的pymssql的whl,然后安装,在pycharm的default setting->project Interpreter中确 ...
- 写接口文档及生成mock数据
写接口文档及生成mock数据 在web应用开发的过程中,与前端联调时总会有一些接口,需要接口文档,在接口先行的情况下,前端不能拿到实际的接口进行开发,所以就需要mock数据. 今天搜索了下,阿里在这方 ...
- [MyBatis]DAO层只写接口,不用写实现类
团队开发一个项目,由老大架了一个框架,遇到了DAO层不用写接口了,我也是用了2次才记住这个事的,因为自己一直都是习惯于写DAO层的实现类,所以,习惯性的还是写了个实现类.于是遇到错误了. 找不到那个方 ...
- node搭建服务器,写接口,调接口,跨域
刚开始学node,今天做这个也是累死宝宝了,以后可以自己写接口自己用了,再也不用麻烦人家后台人员了,这些年我们欠他们的太多了,说多了都是泪,不多说,往下看吧... 服务端项目目录下: 1.npm in ...
- rest风格使用两个变量_为什么要用Rest风格,接口应该怎么定义,除了Rest还可用什么方式写接口的?...
这里是修真院后端小课堂,每篇分享文从 深度思考中的知识点--为什么要用Rest风格,如果不用Rest的话,接口应该怎么定义,在使用Rest风格之前,大家都是用什么方式写接口的? 1.背景介绍 REST ...
- Hibernate_1_配置文件详解_基础案例_Hibernate工具类_API详解_持久化类编写规则
Hibernate( ORM框架 ) Hibernate是一个数据持久化层的ORM框架. 它通过JavaBean, 数据库中的表与自身的映射关系达到表中数据的增删改查 特性 1.对JDBC访问数据库的 ...
- [java基础] --- java开发,service层是不是一定要写接口
估计很多java开发的同学都遇到过,service层要写一个接口,然后再写接口的实现类,但这个接口从项目开始到项目倒闭,都不会有第二个实现,那为什么不直接写个service类呢?如果你还没想过这个问题 ...
最新文章
- android中XMl文件的读取
- 【MM模块】ERS and Invoicing Plans 发票计划
- 计算机与技术卓越信息技术,2018版计算机科学与技术专业(卓越工程师)培养目标与毕业要求...
- python中with是什么意思_python中with的用法
- 解决window8 下连接PLSQL 报ora-12154错误
- linux的tomcat如何开机自启,linux tomcat开机自启
- oracle11g数据库登录01017,sqlplus登录Oracle时ORA-01017: invalid username/password; logon denied的错误...
- 用SLF4j/Logback打印日志-2
- Linux连接Internet
- matlab最速下降法例子,matlab 最速下降法 steepest descent (实例并附有详细说明)
- 什么是分布式操作系统?有哪些优缺点?
- 链接计算机 输入网络密码,联想电脑怎么连接无线网输入密码时怎么输入
- EI: 室内微生物组、大气污染物与幼儿园儿童的哮喘、鼻炎和湿疹—一项重复横断面研究...
- 讲解三层代码讲解(DLL规则层如何接收服务器的数据,又如何交回给服务器)--第四课(*****) DATE :2004-06-01...
- Mac OS 使用笔记
- 塑胶卡扣弹性计算公式_塑胶产品结构设计卡扣
- 如何自己制作CHM电子书?
- 敏捷教练----敏捷项目管理-史诗、故事、主题
- CSS常见选择器的用法
- 全球及中国农药市场需求走势与十四五发展商机研究报告2022版
热门文章
- tensorrt安装_利用TensorRT对深度学习进行加速
- UI设计还在为聊天界面苦恼?好的案例,打开任通二脉
- 立冬节气主题海报你知道怎么做了么?灵感给你,学起来!
- UI设计素材|等轴测图(2.5D插画)
- 国庆海报设计适合哪些精品背景纹理?
- 计算机设备管理程序在哪,怎么打开设备管理器
- html5微信视频禁止自动全屏,关于HTML5 video标签在安卓版微信浏览器内被强行全屏播放的问题...
- ddx_check绑定注意事项
- 2021年二月下旬文章导读与开源项目仓库 | scatter-gather DMA,SR-IOV,ARP欺骗,中断,Lockdep,virtio,vhost
- 算法笔记:简单的字符串模式匹配-KMP算法(与BF算法对比时间复杂度)