C# 用C语言简化组合接口#
在我正在处理的代码中,我有一个结构,其中代码的某些部分取决于当前的软件会话。软件会话包含多个辅助对象,这些对象是由组合注入的依赖项 其中一个例子是IRepository,它包含对数据存储库的访问。IRepository包含一个DatabaseContext,它通过IDbContext再次写入数据库,IDbContext被注入 SoftwareSession是访问数据库的唯一注入式公共基础结构,充当网关。这意味着,当我想将一个对象写入数据库时,例如WriteCar,我必须实现3个接口、2个委托给组合对象的函数和1个带实现的函数。下面的代码片段对此进行了说明。WriteCar签名在3个接口(IRepository、ISoftwareSession、IDbContext)中定义相同,2个未实现的地方(存储库、SoftwareSession)仅调用与合成对象相关的函数,1个实际实现的地方(IDbContext) 这意味着,当我想要重构、移动代码、添加功能或更改函数签名时,我总是需要为一个函数更改6个位置 我认为这为提高可测试性提供了最佳环境,它遵循了软件会话包装对存储库的访问和存储库包装对数据上下文的访问的最佳实践——但我仍然怀疑我们是否有更好的方法编写它一次,或者我是否对下面代码中的某些概念有误解 实现这一点的体系结构上更易于维护的方法是什么?甚至可以使用lambda或委托的一些巧妙方式来减少为每个新功能编写的代码量?或者甚至一些库(比如automapper简化了DTO)或工具来简化使用VisualStudio、Resharper等从某种模板机制生成此代码的过程 请让我知道,如果我在这里有一些概念混乱。我知道我的一些同事有类似的观点,在这种情况下,澄清其他人的误解可能也会有所帮助C# 用C语言简化组合接口#,c#,oop,design-patterns,dependency-injection,delegation,C#,Oop,Design Patterns,Dependency Injection,Delegation,在我正在处理的代码中,我有一个结构,其中代码的某些部分取决于当前的软件会话。软件会话包含多个辅助对象,这些对象是由组合注入的依赖项 其中一个例子是IRepository,它包含对数据存储库的访问。IRepository包含一个DatabaseContext,它通过IDbContext再次写入数据库,IDbContext被注入 SoftwareSession是访问数据库的唯一注入式公共基础结构,充当网关。这意味着,当我想将一个对象写入数据库时,例如WriteCar,我必须实现3个接口、2个委托给组
public class SoftwareSession : ISoftwareSession
{
...
IRepository repository;
public void WriteCar(Car car){
repository.WriteCar(car);
}
...
}
public interface ISoftwareSession{
...
void WriteCar(Car car);
...
}
public class Repository : IRepository{
...
IDbContext context;
public void WriteCar(Car car){
context.WriteCar(car);
}
...
}
public interface IRepository{
...
void WriteCar(Car car);
...
}
public class MyDbContext : IDbContext{
...
public void WriteCar(Car car){
//The Actual Implementation here.
...
}
...
}
public interface IDbContext{
...
void WriteCar(Car car);
...
}
首先,您的
IDbContext
和IRepository
是相同的。您可能希望删除IDbContext
,或者至少从中删除IRepository
中声明的方法
然后,MyDbContext
和Repository
都将实现IRepository
,Repository
类将只是MyDbContext
的包装器
然后,如果Repository
只是将调用转发到MyDbContext
,那么您可能也不需要该类
此外,除了将调用转发到包含的存储库之外,我看不到您在SoftwareSession
中正在做任何事情。您真的需要SoftwareSession
,还是将irepositionory
直接传递给调用会话对象的人有意义
底线是,这个实现充斥着复制和转发。去掉它,您的整个模型就会变得简单。如果看不到您的合成根,我不完全确定您的实现是如何工作的,但我建议您考虑使用控制反转(Inversion of Control,IoC)容器。由于您的
ISoftwareSession
实现只依赖于IRepository
实例,因此您只需要将其注入类的构造函数中。您的IRepository
实现也是如此:您只需要将IDbContext
注入构造函数
使用IoC容器,您可以“注册”,即在应用程序启动时(在组合根目录中)将接口连接到实现,并且当您解析依赖项时,容器负责创建所需的实例。然后,您所要做的就是从容器中获取SoftwareSession
的实例,然后离开
因此,您可以像这样更改SoftwareSession
实现:
public class SoftwareSession : ISoftwareSession
{
IRepository repository;
public SoftwareSession(IRepository repository)
{
this.repository = repository;
}
public void WriteCar(Car car)
{
repository.WriteCar(car);
}
}
public class Repository : IRepository
{
IDbContext context;
public Repository(IDbContext dbContext)
{
context = dbContext;
}
public void WriteCar(Car car)
{
context.WriteCar(car);
}
}
您的存储库
实现如下:
public class SoftwareSession : ISoftwareSession
{
IRepository repository;
public SoftwareSession(IRepository repository)
{
this.repository = repository;
}
public void WriteCar(Car car)
{
repository.WriteCar(car);
}
}
public class Repository : IRepository
{
IDbContext context;
public Repository(IDbContext dbContext)
{
context = dbContext;
}
public void WriteCar(Car car)
{
context.WriteCar(car);
}
}
这是你的作文根:
var ioc = new MyIocContainer();
// register your interfaces and their associated implementation types with the IoC container
ioc.Register<ISoftwareSession, SoftwareSession>();
ioc.Register<IRepository, Repository>();
ioc.Register<IDbContext, MyDbContext>();
// resolve the IoC container
ioc.Resolve();
// get your `ISoftwareSession` instance
var session = ioc.GetConcrete<ISoftwareSession>();
var newCar = new Car();
session.WriteCar(newCar);
var ioc=new MyIocContainer();
//在IoC容器中注册接口及其关联的实现类型
ioc.Register();
ioc.Register();
ioc.Register();
//解析IoC容器
ioc.Resolve();
//获取您的'ISoftwareSession'实例
var session=ioc.GetConcrete();
var newCar=新车();
session.WriteCar(新车);
接口也可以继承!为什么不使用ICarWriter
接口,然后使用IRepository:ICarWriter
、ISoftwareSession:ICarWriter
等等。我认为问题在于软件会话实现,这违反了单一责任,因为在您的框架中,任何东西都会进入数据库、写入卡、写入用户、,等在这个类中必须有一个访问器。@BarryO'Kane我同意-这是一种重构接口的方法-从减少重复代码的角度来看是有意义的。不过,我愿意等待其他可能的简化方法。@Dys1我也同意你的观点,但这不是我们在使用层时使用的一种模式吗(组合优于继承)?数据库层与视图模型分离,视图模型将软件会话用作单独的层/代理,而不是直接访问数据库上下文。如果我们决定更改用于dbcontext的库,例如EF到Ado.net,并进行一些签名更改,我们只在软件会话中更改它。但事实并非如此,我很好奇是否有一种方法可以将其作为约定而不是配置来实现,因为它是可定制的。感谢您的回答。我已经在为问题中的每一项使用带有构造函数注入的Ninject。问题不在于实际注射,而在于药物的使用