我应该有一个单独的接口程序集?

我们目前在一个项目中有相当多的类,每个类都实现了一个接口,主要是为了DI原因。

现在,我个人的感觉是,这些接口应该放在同一个程序集中的一个单独的名称空间中(所以我们有一个MyCompany.CoolApp.DataAccess程序集,其中有一个提供MyCompany.CoolApp.DataAccess.InterfacesInterfaces名称空间)。

但是,有人build议这些接口实际上应该在自己的组装中。 而我的问题是 – 他们是对的? 我可以看到有一些好处(例如,其他项目只需要使用接口程序集),但是在这一天结束时,所有这些程序集都需要加载。 在我看来,可能会有一个稍微复杂的部署问题,因为Visual Studio不会自动将实现程序集拉到目标的bin文件夹中。

有最佳的实践指导方针吗?

编辑:

为了让我的观点更清楚一点:我们已经将UI,DataAccess,DataModel和其他东西分离到不同的程序集中。 我们现在也可以用一个不同的实现来replace我们的实现,而不用担心,因为我们使用Unity(IOC框架)将实现类映射到接口。 我应该指出,除了多态性的原因和为unit testing创​​build模拟外,我们从不写两个相同接口的实现。 所以除了unit testing之外,我们现在不会“换出”一个实现。

我看到在与实现相同的程序集中有接口的唯一缺点是整个程序集(包括未使用的实现)将被加载。

然而,我可以看到在不同的程序集中让他们意味着开发人员不会意外地“实现”新的实现类,而不是使用IOC包装器创build它。

我没有从答案中理解的一点是部署问题。 如果我只是依靠接口程序集,我会有一个像下面的结构:

 MyCompany.MyApplication.WebUI References: MyCompany.MyApplication.Controllers.Interfaces MyCompany.MyApplication.Bindings.Interfaces etc... 

当我build立这个时,自动放入bin文件夹的程序集就是那​​些接口程序集。 然而,我在统一的types映射映射到他们的实际实现不同的接口。 包含我的实现的程序集如何在bin文件夹中结束?

通常的预期 实践是将它们放在自己的程序集中,因为那么使用这些接口的给定项目不需要对这些接口的实现进行硬引用。 从理论上讲,这意味着你可以用很less的痛苦或者没有痛苦来换取执行。

这就是说,我不记得我上次做这个事情了,对@ David_001来说,这不一定是“平常”的。 我们倾向于使我们的接口符合实现,我们最常用于正在testing的接口。

我认为有不同的立场取决于你正在生产的东西。 我倾向于生成LOB应用程序,这些应用程序需要与其他应用程序和团队进行内部互操作,所以有一些利益相关者可以使用任何给定应用程序的公共API。 但是,对于许多未知的客户来说,这样做并不像为公共API提供一个图书馆或框架那样极端重要。

在部署场景中,如果您更改了实现,则理论上只需部署该单个DLL即可,例如仅留下UI和接口DLL。 如果您一起编译了接口和实现,则可能需要重新部署UI DLL …

另一个好处是你的代码的清理隔离 – 有一个接口(或共享库)DLL明确地向开发团队中的任何位置放置新的types等。我不再指望这是一个好处,因为我们还没有任何不这样做的问题,公共契约仍然很容易find,无论接口放置在哪里。

我不知道是否有最佳做法是赞成或反对的,重要的是,在代码中,您总是使用接口,并且不会泄露任何代码泄漏到实现中。

到目前为止的答案似乎是说,把接口放在自己的组件中是“惯常”的做法。 我不同意将不相关的接口放入一个“共享”的通用程序集,所以这意味着我需要为每个“实现”程序集设置一个接口程序集。

然而,进一步思考这个问题,我想不出许多真实世界的例子(例如log4net或NUnit提供了公共接口程序集,以便消费者可以决定不同的实现吗?如果是这样的话,那么nunit的其他实现我用?)。 花费年龄看谷歌,我已经find了一些资源。

我所谓的共享types(我也使用DI)遵循的模式是有一个单独的程序集,其中包含以下用于应用程序级别的概念(而不是普通概念):

  1. 共享的接口。
  2. DTO的。
  3. 例外。

通过这种方式,可以pipe理客户端和核心应用程序库之间的依赖关系,因为客户端不能直接依赖具体的实现,或者直接添加直接的程序集引用,然后访问任何旧的公共types。

然后我有一个运行时typesdevise,在应用程序启动时设置了我的DI容器,或者是一套unit testing的开始。 通过这种方式,实现之间有明确的分离,以及如何通过DI来改变它们。 我的客户端模块从来没有直接引用实际的核心库,只有“SharedTypes”库。

我的devise的关键是为客户端(无论是WPF应用程序还是NUnit)提供了一个通用的运行时概念,用于设置所需的依赖关系,即具体实现或某种types的mock \ stubs。

如果上面的共享types没有被分解出来,而是客户端使用具体的实现来添加对程序集的引用,那么对于客户来说,使用具体的实现而不是接口是非常容易的。 随着时间的推移逐渐结束,逐渐变得非常容易,不需要付出很大的努力,更重要的是时间就几乎不可能解决。

更新

用一个例子说明依赖关系如何在目标应用程序中结束。

在我的情况下,我有一个WPF客户端应用程序。 我使用棱镜和统一(DI),重要的是,棱镜是用于应用程序的组成。

使用Prism,您的应用程序集合只是一个Shell,实际的function实现驻留在“模块”程序集(您可以为每个概念模块有一个单独的程序集,但这不是要求,我有一个模块程序集ATM)。 加载模块是shell的责任 – 这些模块的组成应用程序。 模块使用SharedTypes程序集,但是shell引用具体程序集。 我讨论的运行时typesdevise负责初始化依赖关系,这在Shell中完成。

这样具有所有function的模块组件不依赖于具体的实现。 它们是由依赖关系sorting的shell加载的。 shell引用具体的程序集,这就是它们如何进入bin目录。

依赖性草图:

 Shell.dll <-- Application --ModuleA.dll --ModuleB.dll --SharedTypes.dll --Core.dll --Common.dll + Unity.dll <-- RuntimeDI ModuleA.dll --SharedTypes.dll --Common.dll + Unity.dll <-- RuntimeDI ModuleB.dll --SharedTypes.dll --Common.dll + Unity.dll <-- RuntimeDI SharedTypes.dll --... 

我同意打勾的答案。 对你有好处,大卫。 事实上,我很欣慰地看到答案,认为我疯了。

我一直在企业C#自由职业中看到这个有趣的“笔筒式钢笔”模式,人们遵循人群规则,团队必须遵守,不符合就是闹事。

另一个疯狂的是每个程序集废话的一个命名空间。 所以你得到一个SomeBank.SomeApp.Interfaces命名空间, 一切都在其中。

对我来说,这意味着types分散在命名空间和程序集中,包含我不关心的全部东西必须在整个地方被引用。

至于接口,我甚至不使用我的私人应用程序接口; DI适用于types,具体的虚拟,基类或接口。 我相应地select并根据它们的作用将types放在DLL中。

我以后从来没有遇到DI或交换逻辑的问题。

.NET程序集是安全性,API范围和部署的一个单元,与名称空间无关。

•如果两个程序集相互依赖,则不能单独部署和版本化,应该合并。

•拥有许多DLL往往意味着要公开大量的东西,以至于很难将types成员公开的API公开化,因为它们被任意地放在自己的程序集中。

•我的DLL之外的代码是否需要使用我的types?

•保守; 我通常可以很容易地将一个types移出一个图层,反之则更难一些。

•我是否可以将我的function区域或框架整齐地封装到NuGet包中,使其像任何其他包一样是完全可选和可版本化的?

•我的types是否alignment到一个function的交付,并可以放在一个function命名空间?

•许多真正的库和框架被打上烙印,使它们易于讨论,并且它们不会烧毁名称空间名称,暗示它的使用或含糊不清,我可以使用“代码名称”(如Steelcore)来代替通用types化应用程序的组件陈词滥调,errm“服务”?

编辑

这是我今天看到的发展中被误解的事情之一。 太糟糕了

你有一个API,所以把它的所有types放在单个API项目中。 只有当你需要共享/重用它们时,才将它们移出。 当你把它们移出来的时候,把它们直接移动到一个带有明确名称的NuGet软件包,这个名字包含了软件包的意图和焦点。 如果你为一个名字而苦苦挣扎,并且考虑到“普通”,那可能是因为你正在创build一个垃圾场。

你应该把你的NuGet软件包纳入一系列相关的软件包中。 你的“核心”软件包应该对其他软件包的依赖性最小。 里面的types是相关的使用和相互依赖。

然后为需要更多依赖关系的更专门的types和子types创build一个新的包; 更清楚的是:你可以通过外部的依赖来分割一个库,而不是通过types或者是一个接口还是一个exception。

图书馆保理

所以你可能会把所有的types都放在一个大型的库中,但是一些更特殊的types(彩色点)依赖于某些外部库,所以现在你的库需要引入所有这些依赖关系。 这是不必要的,你应该把这些types分解成进一步专门的库,这些库需要依赖关系。

包A和B中的types可以属于同一个名称空间。 引用A引入了一组types,然后可选地引用B更多地补充了名称空间。

而已。

卢克

我正在查看对象浏览器中的System.Data.dll(4.0),我可以看到它本身是自治的,不只是接口,而是所有的工具类,如DataSet,DataTable,DataRow,DataColumn等。 此外,略过了System.Data,System.Data.Common,System.Configuration&System.Xml等名称空间列表,它build议首先将接口包含在它们自己的程序集中,并将所有相关和必需的代码放在一起,更重要的是在整个应用程序(或框架)中重复使用相同的名称空间,以便虚拟分离类。