架构问题:使用依赖注入导致垃圾API

时间:2011-03-11 11:04:03

标签: c# api architecture dependency-injection castle-windsor

我想创建一个类来执行各种与数据库相关的低级操作,但是它提供了一个非常简单的UI层接口。

此类表示特定聚合根中的一组数据,由单个ID int检索。

构造函数有四个参数:

public AssetRegister(int caseNumber, ILawbaseAssetRepository lawbaseAssetRepository, IAssetChecklistKctcPartRepository assetChecklistKctcPartRepository, User user)
{
  _caseNumber = caseNumber;
  _lawbaseAssetRepository = lawbaseAssetRepository;
  _assetChecklistKctcPartRepository = assetChecklistKctcPartRepository;
  _user = user;
  LoadChecklists();
}

UI层通过接口IAssetRegister访问此类。 Castle Windsor本身可以提供ILawbaseAssetRepository和IAssetChecklistKctcPartRepository参数,但UI代码需要使用匿名类型提供其他两个:

int caseNumber = 1000;
User user = GetUserFromPage();
IAssetRegister assetRegister = Moose.Application.WindsorContainer.Resolve<IAssetRegister>(new { caseNumber, user});

从API设计的角度来看,这是垃圾。 UI层开发人员无法知道IAssetRegister需要整数和用户。他们需要知道类的实现才能使用它。

我知道我必须在这里遇到某种设计问题。谁能给我一些指示?

2 个答案:

答案 0 :(得分:3)

尝试将消息与行为分开。创建一个包含操作数据的类,并创建一个包含该操作的业务逻辑的不同类。例如,创建此命令:

public class RegisterAssetCommand
{
    [Required]
    public int CaseNumber { get; set; }

    [Required]
    public User Operator { get; set; }
}

现在定义一个用于处理业务命令的接口:

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

您的演示文稿代码现在将如下所示:

var command = new RegisterAssetCommand
{
    CaseNumber = 1000,
    Operator = GetUserFromPage(),
};

var commandHandler = WindsorContainer
    .Resolve<ICommandHandler<RegisterAssetCommand>);

commandHandler.Handle(command);

注意: 如果可能,请将表示类中的commandHandler移出并将其注入该类的构造函数(再次构造函数注入)。< / em>的

不,您可以像这样创建ICommandHandler<RegisterAssetCommand>的实现:

public class RegisterAssetCommandHandler
    : ICommandHandler<RegisterAssetCommand>
{
    private ILawbaseAssetRepository lawbaseAssetRepository;
    private IAssetChecklistKctcPartRepository assetRepository;

    public RegisterAssetCommandHandler(
        ILawbaseAssetRepository lawbaseAssetRepository,
        IAssetChecklistKctcPartRepository assetRepository)
    {
        this.lawbaseAssetRepository = lawbaseAssetRepository;
        this.assetRepository = assetRepository;
    }

    public void Handle(RegisterAssetCommand command)
    {
        // Optionally validate the command

        // Execute the command
    }
}

或者,您甚至可以通过在User中注入RegisterAssetCommand,将IUserProvider退出RegisterAssetCommandHandlerIUserProvider接口可以有一个GetUserForCurrentContext处理程序可以调用。

我希望这是有道理的。

答案 1 :(得分:2)

正如Morten所指出的那样,将非注射依赖从构造函数调用移动到实际需要使用它的方法,

如果你有不能(或很难)注入的构造函数参数,你将无法自动将IAssetRegister注入任何需要它的类。

当然,您可以随时创建一个IUserProvider接口,其中包含以下几行具体实现:

public class UserProvider : IUserProvider 
{
    // interface method
    public User GetUser() 
    {
        // you obviously don't want a page dependency here but ok...
        return GetUserFromPage();
    }
}

因此创建另一个可注射的依赖项,其中没有。现在,您无需将用户传递给可能需要它的每个方法。