简化C#中的组合界面

时间:2017-02-14 15:36:40

标签: c# oop design-patterns dependency-injection delegation

在我正在处理的代码中,我有一个结构,其中代码的某些部分取决于当前的软件会话。软件会话包含多个辅助对象,这些对象是由合成注入的依赖项。

一个例子是IRepository注入它,它包含对数据存储库的访问。并且IRepository包含一个DatabaseContext,它通过再次注入的IDbContext写入数据库。

SoftwareSession是唯一注入通用基础设施,用于访问数据库,充当网关。这意味着当我想将对象写入数据库时​​,例如WriteCar,我将不得不实现3个接口,2个函数委托给组合对象,1个函数与实现。它在下面的代码片段中阐明。 WriteCar签名在3个接口(IRepository,ISoftwareSession,IDbContext)中定义相同,2个未实现的地方(Repository,SoftwareSession),它只调用合成对象相关函数和1个实际实现位置(IDbContext)

这意味着当我想重构,移动代码,添加功能或更改功能签名时,我将不得不为一个功能更改6个位置。

我认为这提供了提高可测试性的最佳环境,并遵循软件会话包含对存储库的访问权限和存储库包装对数据上下文的访问的最佳实践 - 但我仍然质疑我们是否可以有更好的方法来编写一次,或者我对下面的代码中的某些概念有误解?

在架构上更易于维护的实现方式是什么?甚至可能使用一些聪明的lambda或委托方式来减少为每个新功能编写的代码量?甚至一些库(如automapper简化DTO)或工具可以使用Visual Studio,Resharper等从某种模板机制中轻松生成此代码?

如果我在这里遇到一些混乱的概念,请告诉我。我知道我的一些同事也有类似的看法,在这种情况下澄清别人的误解可能会有所帮助。

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);
    ...
}

2 个答案:

答案 0 :(得分:1)

首先,您的IDbContextIRepository是相同的。您可能希望删除IDbContext,或者至少删除IRepository中声明的方法。

然后,MyDbContextRepository都会实现IRepository,而Repository类只会成为MyDbContext的包装。

然后,如果Repository仅将呼叫转发到MyDbContext,那么您可能也不需要该类。

此外,除了将调用转发到包含的存储库之外,我没有看到您在SoftwareSession中执行任何操作。您真的需要SoftwareSession,还是将IRepository直接传递给调用会话对象的人是否有意义?

最重要的是,这种实现正在重复和转发。删除它,你的整个模型将变得简单。

答案 1 :(得分:1)

如果没有看到你的作品根,我不完全确定你的实现是如何工作的,但我建议使用控制反转(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);
    }
}

您的Repository实施方式如下:

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);