架构问题:使用dependency injection导致垃圾API

我想创建一个类来执行各种与数据库相关的低级操作,但是它提供了一个非常简单的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(new { caseNumber, user}); 

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

我知道我必须在这里遇到某种设计问题。 任何人都可以给我一些指示吗?

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

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

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

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

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

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

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

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

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

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

 var command = new RegisterAssetCommand { CaseNumber = 1000, Operator = GetUserFromPage(), }; var commandHandler = WindsorContainer .Resolve); commandHandler.Handle(command); 

注意: 如果可能的话,将使得commandHandler的责任移出表示类并将其注入该类的构造函数(构造函数再次注入)。

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

 public class RegisterAssetCommandHandler : ICommandHandler { 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 } } 

或者,您甚至可以通过在RegisterAssetCommand注入IUserProvider来使User退出RegisterAssetCommandHandlerIUserProvider接口可以具有处理程序可以调用的GetUserForCurrentContext

我希望这是有道理的。