为什么我们在 C# 中需要 UI、业务和数据访问之间的接口
why do we require interfaces between UI,Business and Data access in C#
我在很多地方看到,当c#程序员使用三层架构时,他们倾向于在每一层之间使用接口。例如,如果解决方案是
SampleUI
Sample.Business.Interface
Sample.Business
Sample.DataAccess.Interface
Sample.DataAccess
这里UI通过接口调用业务层,业务调用数据访问也是一样的。
如果这种方法是为了减少层之间的依赖性,那么它已经与 class 库一起使用,而无需额外使用接口。
代码示例如下,
Sample.Business
public class SampleBusiness{
ISampleDataAccess dataAccess = Factory.GetInstance<SampleDataAccess>();
dataAccess.GetSampledata();
}
Sample.DataAccess.Interface
public interface IsampleDataAccess{
string GetSampleData();
}
Sample.DataAccess
public class SampleDataAccess:ISampleDataAccess{
public string GetSampleData(){
returns data;// data from database
}
}
- 中间的这个推论有什么用吗?
- 如果我使用 newSampleDataAccess().SampleData() 并删除完整的接口 class 库会怎么样?
你应该总是使用层的抽象来拥有能力
- 模拟单元测试中的接口
- 使用假实现来加快开发速度
- 轻松开发替代实现
- 在不同的实现之间切换
- ...
代码契约
在设计过程中使用接口有一个显着的优势:它是一种契约。
接口是合同规范,在以下意义上:
如果我使用(使用)接口,我会限制自己使用接口公开的内容。好吧,除非我想玩肮脏的游戏(反射等)。
如果我实现接口,我将限制自己提供接口公开的内容。
以这种方式做事的好处是它简化了开发团队在层之间的工作划分。它允许一层的开发人员提供一个 cough 接口 cough 下一层可以用来与之通信......甚至在这样的接口之前已实施
一旦他们就接口达成一致。至少在最小可行界面上。他们可以开始并行开发这些层,因为知道另一个团队会维护他们在合同中的部分。
嘲讽
以这种方式使用接口的一个副作用是它允许mock 组件的实现。这简化了单元测试的创建。这样你就可以单独测试一个层的实现。因此,您可以轻松区分某层因缺陷而失效,以及某层因其下层缺陷而失效。
对于由一个人开发的项目,或者由一个不太在意划清界限来分隔工作的团队开发的项目,mock 的能力可能是他们实现接口的主要动机。
例如,如果您想测试您的表示层是否可以正确处理分页...但是您需要请求数据来填充这些页面。可能是这样的:
- 下面的图层还没有准备好。
- 数据库还没有数据可以提供。
- 它失败了,他们不知道分页代码是否正确缺陷来自代码更深的地方
- 等等……
无论哪种方式,解决方案都是模拟的。此外,如果你有模拟接口,模拟会更容易。
更改实现
如果某些开发人员出于某种原因决定他们想要更改其层的实现,他们可以信任接口强加的合同。这样,他们可以交换实现而无需更改其他层的代码。
什么原因?
也许他们想测试一项新技术。在这种情况下,他们可能会创建一个替代实现作为实验。此外,他们希望两个版本都能正常工作,以便测试哪个版本效果更好。
附录:不仅可以测试两个版本,还可以轻松回滚到主版本。当然,他们可能会通过源版本控制来实现这一点。因此,我不会考虑将回滚作为使用接口的动机。然而,对于任何不使用版本控制的人来说,这可能是一个优势。对于任何不使用它的人...开始使用它吧!
或者他们可能需要将代码移植到不同的平台或不同的数据库引擎。在这种情况下,他们可能也不想丢弃旧代码...例如,如果他们有 运行 Windows 和 SQL 服务器的客户端以及其他 运行 Linux 和 Oracle,维护两个版本是有意义的。
当然,在任何一种情况下,您都希望能够通过尽可能少的工作来实施这些更改。因此,您不想更改上面的层以针对不同的实现。相反,您可能会有某种形式的 factory 或 inversion of control container,您可以配置它们来执行 dependency injection 与你想要的实现。
缓解变更传播
当然,他们可能会决定更改实际界面。如果在一个层上工作的开发人员需要在界面上添加一些额外的东西,他们可以将它添加到界面中(考虑到团队已经设置的任何方法来批准这些更改)而不会弄乱 类 的代码其他团队正在努力。在源版本控制中,这将缓解边距变化。
最后,使用分层架构的目的是关注点分离。这意味着更改原因的分离......如果您需要更改数据库,您的更改不应传播到专门用于向用户提供信息的代码中。当然,团队可以用具体的 类 来完成这个任务。然而,接口提供了一个很好的、明显的、定义良好的、支持语言的障碍来阻止变化的传播。特别是如果团队有关于责任的良好规则(不,我不是说代码问题,我的意思是,开发人员负责做什么)。
我在很多地方看到,当c#程序员使用三层架构时,他们倾向于在每一层之间使用接口。例如,如果解决方案是
SampleUI
Sample.Business.Interface
Sample.Business
Sample.DataAccess.Interface
Sample.DataAccess
这里UI通过接口调用业务层,业务调用数据访问也是一样的。 如果这种方法是为了减少层之间的依赖性,那么它已经与 class 库一起使用,而无需额外使用接口。 代码示例如下,
Sample.Business
public class SampleBusiness{
ISampleDataAccess dataAccess = Factory.GetInstance<SampleDataAccess>();
dataAccess.GetSampledata();
}
Sample.DataAccess.Interface
public interface IsampleDataAccess{
string GetSampleData();
}
Sample.DataAccess
public class SampleDataAccess:ISampleDataAccess{
public string GetSampleData(){
returns data;// data from database
}
}
- 中间的这个推论有什么用吗?
- 如果我使用 newSampleDataAccess().SampleData() 并删除完整的接口 class 库会怎么样?
你应该总是使用层的抽象来拥有能力
- 模拟单元测试中的接口
- 使用假实现来加快开发速度
- 轻松开发替代实现
- 在不同的实现之间切换
- ...
代码契约
在设计过程中使用接口有一个显着的优势:它是一种契约。
接口是合同规范,在以下意义上:
如果我使用(使用)接口,我会限制自己使用接口公开的内容。好吧,除非我想玩肮脏的游戏(反射等)。
如果我实现接口,我将限制自己提供接口公开的内容。
以这种方式做事的好处是它简化了开发团队在层之间的工作划分。它允许一层的开发人员提供一个 cough 接口 cough 下一层可以用来与之通信......甚至在这样的接口之前已实施
一旦他们就接口达成一致。至少在最小可行界面上。他们可以开始并行开发这些层,因为知道另一个团队会维护他们在合同中的部分。
嘲讽
以这种方式使用接口的一个副作用是它允许mock 组件的实现。这简化了单元测试的创建。这样你就可以单独测试一个层的实现。因此,您可以轻松区分某层因缺陷而失效,以及某层因其下层缺陷而失效。
对于由一个人开发的项目,或者由一个不太在意划清界限来分隔工作的团队开发的项目,mock 的能力可能是他们实现接口的主要动机。
例如,如果您想测试您的表示层是否可以正确处理分页...但是您需要请求数据来填充这些页面。可能是这样的:
- 下面的图层还没有准备好。
- 数据库还没有数据可以提供。
- 它失败了,他们不知道分页代码是否正确缺陷来自代码更深的地方
- 等等……
无论哪种方式,解决方案都是模拟的。此外,如果你有模拟接口,模拟会更容易。
更改实现
如果某些开发人员出于某种原因决定他们想要更改其层的实现,他们可以信任接口强加的合同。这样,他们可以交换实现而无需更改其他层的代码。
什么原因?
也许他们想测试一项新技术。在这种情况下,他们可能会创建一个替代实现作为实验。此外,他们希望两个版本都能正常工作,以便测试哪个版本效果更好。
附录:不仅可以测试两个版本,还可以轻松回滚到主版本。当然,他们可能会通过源版本控制来实现这一点。因此,我不会考虑将回滚作为使用接口的动机。然而,对于任何不使用版本控制的人来说,这可能是一个优势。对于任何不使用它的人...开始使用它吧!
或者他们可能需要将代码移植到不同的平台或不同的数据库引擎。在这种情况下,他们可能也不想丢弃旧代码...例如,如果他们有 运行 Windows 和 SQL 服务器的客户端以及其他 运行 Linux 和 Oracle,维护两个版本是有意义的。
当然,在任何一种情况下,您都希望能够通过尽可能少的工作来实施这些更改。因此,您不想更改上面的层以针对不同的实现。相反,您可能会有某种形式的 factory 或 inversion of control container,您可以配置它们来执行 dependency injection 与你想要的实现。
缓解变更传播
当然,他们可能会决定更改实际界面。如果在一个层上工作的开发人员需要在界面上添加一些额外的东西,他们可以将它添加到界面中(考虑到团队已经设置的任何方法来批准这些更改)而不会弄乱 类 的代码其他团队正在努力。在源版本控制中,这将缓解边距变化。
最后,使用分层架构的目的是关注点分离。这意味着更改原因的分离......如果您需要更改数据库,您的更改不应传播到专门用于向用户提供信息的代码中。当然,团队可以用具体的 类 来完成这个任务。然而,接口提供了一个很好的、明显的、定义良好的、支持语言的障碍来阻止变化的传播。特别是如果团队有关于责任的良好规则(不,我不是说代码问题,我的意思是,开发人员负责做什么)。