具体来说,当您创建一个接口/实现器对,并且没有压倒一切的组织问题(例如接口应该放在不同的程序集中,即按照 s# 架构的建议)时,您是否有一种默认的方式来组织它们?命名空间/命名方案?
这显然是一个更多基于意见的问题,但我认为有些人对此进行了更多思考,我们都可以从他们的结论中受益。
具体来说,当您创建一个接口/实现器对,并且没有压倒一切的组织问题(例如接口应该放在不同的程序集中,即按照 s# 架构的建议)时,您是否有一种默认的方式来组织它们?命名空间/命名方案?
这显然是一个更多基于意见的问题,但我认为有些人对此进行了更多思考,我们都可以从他们的结论中受益。
答案取决于你的意图。
我敢肯定还有其他选择,但与大多数命名空间问题一样,它归结为项目的用例,以及它包含的类和接口。
我通常将接口保存在与具体类型相同的命名空间中。
但是,这只是我的看法,命名空间布局是非常主观的。
Animals
|
| - IAnimal
| - Dog
| - Cat
Plants
|
| - IPlant
| - Cactus
将一两种类型移出主命名空间并没有真正获得任何好处,但确实添加了一个额外的 using 语句的要求。
我通常做的是在我的层次结构的较高级别创建一个接口命名空间并将所有接口放在那里(我不会费心在其中嵌套其他命名空间,因为我最终会得到许多只包含一个接口的命名空间)。
Interfaces
|--IAnimal
|--IVegetable
|--IMineral
MineralImplementor
Organisms
|--AnimalImplementor
|--VegetableImplementor
这正是我过去所做的方式,并且我没有遇到很多问题,尽管诚然,这可能会让其他坐下来处理我的项目的人感到困惑。我很好奇看看其他人是怎么做的。
我更喜欢将我的接口和实现类放在同一个命名空间中。如果可能,我会为实现类提供内部可见性并提供一个工厂(通常以静态工厂方法的形式委托给工作人员类,并提供一个内部方法,该方法允许在友元程序集中进行单元测试来替换另一个工作人员产生存根)。当然,如果具体类需要是公共的——例如,如果它是一个抽象基类,那也没关系;我看不出有任何理由将 ABC 放在自己的命名空间中。
附带说明一下,我非常不喜欢在接口名称前加上字母“I”的 .NET 约定。(I)Foo 接口模型的东西不是ifoo,它只是一个foo。那么为什么我不能直接称它为 Foo 呢?然后我具体命名实现类,例如 AbstractFoo、MemoryOptimizedFoo、SimpleFoo、StubFoo 等。
(.Net) 我倾向于将接口保留在一个单独的“通用”程序集中,这样我就可以在多个应用程序中使用该接口,更常见的是,在我的应用程序的服务器组件中使用该接口。
关于名称空间,我将它们保存在 BusinessCommon.Interfaces 中。
我这样做是为了确保我和我的开发人员都不想直接引用这些实现。
以某种方式分离接口(Eclipse 中的项目等),以便只部署接口。这允许您在不提供实现的情况下提供外部 API。这允许依赖项目以最少的外部构建。显然,这更适用于大型项目,但这个概念在所有情况下都是好的。
我通常将它们分成两个单独的程序集。接口的一个常见原因是让一系列对象在您的软件的某些子系统中看起来相同。例如,我的所有报表都实现了 IReport 接口。IReport 不仅用于打印,还用于预览和选择每个报告的单个选项。最后,我有一组 IReport 可在对话框中使用,用户可以在其中选择他们想要打印的报告(和配置选项)。
报表驻留在单独的程序集中,而 IReport、预览引擎、打印引擎、报表选择驻留在它们各自的核心程序集和/或 UI 程序集中。
如果您使用工厂类返回报表程序集中的可用报表列表,那么使用新报表更新软件只需在原始报表程序集上复制新报表程序集即可。您甚至可以使用反射 API 来扫描任何报告工厂的程序集列表并以这种方式构建您的报告列表。
您也可以将此技术应用于文件。我自己的软件运行金属切割机,因此我们将这个想法用于与我们的软件一起销售的形状和配件库。
同样,实现核心接口的类应该驻留在单独的程序集中,以便您可以与软件的其余部分分开更新它。
我讨厌在同一个命名空间/程序集中找到接口和实现。请不要那样做,如果项目发展了,重构是一件很痛苦的事情。
当我引用一个接口时,我想实现它,而不是获取它的所有实现。
我可以接受的是将接口与其依赖类(引用接口的类)。
编辑:@Josh,我刚刚读了我的最后一句话,这令人困惑!当然,依赖类和实现它的类都引用了该接口。为了让自己清楚,我将举例说明:
可接受:
接口+实现:
namespace A;
Interface IMyInterface
{
void MyMethod();
}
namespace A;
Interface MyDependentClass
{
private IMyInterface inject;
public MyDependentClass(IMyInterface inject)
{
this.inject = inject;
}
public void DoJob()
{
//Bla bla
inject.MyMethod();
}
}
实现类:
namespace B;
Interface MyImplementing : IMyInterface
{
public void MyMethod()
{
Console.WriteLine("hello world");
}
}
不能接受的:
namespace A;
Interface IMyInterface
{
void MyMethod();
}
namespace A;
Interface MyImplementing : IMyInterface
{
public void MyMethod()
{
Console.WriteLine("hello world");
}
}
请不要为您的界面创建项目/垃圾!示例:ShittyProject.Interfaces。你错过了重点!
想象一下,您创建了一个为您的接口保留的 DLL (200 MB)。如果您必须使用两行代码添加一个界面,您的用户将不得不为两个愚蠢的签名更新 200 MB!
我给出了与其他答案相反的自己的经验。
我倾向于将我所有的接口放在它们所属的包中。这意味着,如果我在另一个项目中移动一个包,我将拥有运行包所需的所有东西,而无需进行任何更改。
对我来说,作为类功能一部分的任何辅助函数和操作符函数都应该与类进入相同的命名空间,因为它们构成了该命名空间的公共 API 的一部分。
如果您有在不同包中共享相同接口的通用实现,您可能需要重构您的项目。
有时我看到项目中有很多接口可以转换为抽象实现而不是接口。
所以,问问自己你是否真的在为类型或结构建模。