我想创建一个类来执行各种与数据库相关的低级操作,但是它提供了一个非常简单的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需要整数和用户。他们需要知道类的实现才能使用它。
我知道我必须在这里遇到某种设计问题。谁能给我一些指示?
答案 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
退出RegisterAssetCommandHandler
。 IUserProvider
接口可以有一个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();
}
}
因此创建另一个可注射的依赖项,其中没有。现在,您无需将用户传递给可能需要它的每个方法。