IOC and interfaces
本文关键字:interfaces and IOC | 更新日期: 2023-09-27 18:11:00
我有一个像这样的项目结构:-
CentralRepository.BL
CentralRepository.BO
CentralRepository.DataAccess
CentralRepository.Tests
CentralRepository.Webservices
之间有很多依赖关系。我想利用unity来减少依赖,所以我要为我的类创建接口。我的问题是接口应该驻留在哪个项目中。我的想法是它们应该在BO层。有人能给我一些指导吗?
在组合层面上,你有三个选择:
- 在单独的库中定义接口
- 定义接口及其消费者
- 定义接口及其实现者
然而,最后一个选项是一个非常糟糕的主意,因为它将接口与实现者紧密耦合(或者相反)。由于首先引入接口的全部意义在于减少耦合,因此这样做没有任何好处。
与消费者一起定义接口通常就足够了,我个人只在不同的消费者起作用时才采取额外的步骤,在单独的库中定义接口(如果您发布公共API,通常会发生这种情况)。
BO本质上是您的域对象,至少这是我的假设。一般来说,除非你使用像ActiveRecord这样的模式,否则它们只是状态对象。另一方面,接口指定行为。从许多"最佳实践"来看,将行为和状态混合在一起不是一个好概念。现在我可能会漫谈一下,但我认为背景可能会有所帮助。
现在,对于接口应该存在于何处的问题。有几个选择。
- 将接口固定在它们所属的库中。
- 创建一个单独的合同库
更简单的方法是将它们放在同一个库中,但是您的模拟依赖于库以及您的测试。没什么大不了的,但它有一点气味。
我通常的方法是这样设置项目:
{公司},{计划/项目}。(可选)}{担忧。}{区域。{分区(可选)}
名称的前两到三位由"CentralRepository"一词覆盖。对我来说,就是我的公司。中央存储库或MyCompany.MyProgram。中央存储库,但命名约定不是本文的核心部分。
"区域"部分是这篇文章的重点,我通常使用以下内容。
- 设置一个域对象库(您的BO): CentralRepository.Domain.Models
- 设置一个域异常库:CentralRepository.Domain.Exceptions
- 所有/大多数其他项目都引用了上面两个,因为它们代表了应用程序中的状态。当然,所有业务库都使用这些对象。持久性库可能有不同的模型,我可能在体验库上有一个视图模型。 接下来设置核心库:CentralRepository。核心(可能有子区域?)这是业务逻辑所在的地方(实际的应用程序,因为持久性和体验的变化不应该影响核心功能)。
- 为core设置一个测试库:CentralRepository.Core.Test.Unit.VS。VS表示这些是单元测试,而不是与单元测试库的集成测试,我使用VS表示MSTest(其他人会有不同的命名)。
- 创建测试,然后设置业务功能。根据需要设置接口。示例
- 需要来自DAL的数据,因此为用于核心测试的数据设置了接口和mock。这里的名称类似于centralrepository . persistent . contracts(如果有多种类型的持久性,也可以使用子区域)。
这里的核心概念是"核心即应用程序",而不是n层(它们是兼容的,但只考虑业务逻辑,作为范例,使您与持久性和经验松散耦合)。
现在,回到你的问题。我设置接口的方式是基于"接口"类的位置。因此,我可能会输入:CentralRepository.Core.ContractsCentralRepository.Experience.Service.ContractsCentralRepository.Persist.Service.ContractsCentralRepository.Persist.Data.Contracts
我仍然在处理这个问题,但核心概念是我的IoC和测试都应该被考虑,我应该能够隔离测试,如果我能隔离契约(接口),这将更好地实现。逻辑分离很好(单个库),但我通常不会采用这种方式,因为至少有几个新手开发人员发现,如果没有物理分离,很难看到逻辑分离。你的里程可能会有所不同。: 0
希望这些漫谈在某种程度上有所帮助。
如果您谈论的是程序集,我建议在大多数情况下将接口保留在其实现者所在的地方。
就我个人而言,当我使用分层方法时,我倾向于为每个层提供自己的组件,并为其提供对其下一层的引用。在每一层中,大多数公共事物都是接口。因此,在数据访问层,我可能有iccustomerdao和IOrderDao作为公共接口。我还将在Dao组装中使用公共Dao工厂。然后,我将使用标记为内部的特定实现——CustomerDaoMySqlImpl或CustomerDaoXmlImpl来实现公共接口。然后,公共工厂向用户(即领域层)提供实现,而用户不知道他们得到的是哪个实现——他们向工厂提供信息,工厂反过来给他们一个他们使用的iccustomerdao。
我提到所有这些的原因是为理解接口真正应该是什么奠定基础——API的服务端和客户端之间的契约。因此,从依赖关系的角度来看,您希望通常在服务所在的位置定义契约。如果你在其他地方定义它,你可能并没有真正管理与接口的依赖关系,而只是引入了一个无用的间接层。
所以,无论如何,我想说的是,把你的接口想象成它们是什么——你将提供什么给你的客户的合同,同时保持你将如何提供它的隐私细节。这可能是一个很好的启发式,它将使放置界面的位置更直观。