我想注入构造函数参数IActionLogger actionLogger,但是希望其他参数largeBucket,smallBucket和amountToRetrieve是上下文敏感的(不确定这是否是正确的术语)。
问题:
我是否应该将这些构造函数参数设置为自动属性,并将IActionLogger actionLogger参数保留在构造函数中?
基本上,计算会根据largeBucket,smallBucket和amountToRetrieve变量而有所不同? 我把这些变量放在构造函数中,因为我需要事先做一些设置。
public class BucketActionsHandler : IBucketActionsHandler
{
private List<IAction> _actions = new List<IAction>();
private Bucket _largeBucket;
private Bucket _smallBucket;
private IActionLogger _actionLogger;
private int _amountToRetrieve;
public BucketActionsHandler(Bucket largeBucket, Bucket smallBucket, int amountToRetrieve, IActionLogger actionLogger)
{
_largeBucket = largeBucket;
_smallBucket = smallBucket;
_amountToRetrieve = amountToRetrieve;
_actionLogger = actionLogger;
_actions.Add(new LastAction(largeBucket, smallBucket, amountToRetrieve));
_actions.Add(new EmptySmallerBucketAction(largeBucket, smallBucket, amountToRetrieve));
_actions.Add(new EmptyLargeBucketAction(largeBucket, smallBucket, amountToRetrieve));
_actions.Add(new FillLargeBucketAction(largeBucket, smallBucket, amountToRetrieve));
_actions.Add(new FillSmallBucketAction(largeBucket, smallBucket, amountToRetrieve));
_actions.Add(new TransferToLargeBucketAction(largeBucket, smallBucket, amountToRetrieve));
_actions.Add(new TransferToSmallBucketAction(largeBucket, smallBucket, amountToRetrieve));
}
private IAction GetNextAction()
{
foreach (var action in _actions)
{
if (action.SatisfiedCondition())
{
return action;
}
}
return null;
}
public void CalculateSteps()
{
IAction nextAction;
do
{
nextAction = GetNextAction();
nextAction.Execute();
if (nextAction == null)
{
throw new InvalidOperationException("No valid action available");
}
} while(!(nextAction is LastAction));
}
}
答案 0 :(得分:1)
我是否应该将这些构造函数参数设置为自动属性
不,因为这会允许您在注入或创建后更改此服务,这是一件坏事,因为服务应该是无状态的,或者至少它们的内部状态更改应该对应用程序的正确性没有影响。当您更改服务的状态时,应用程序代码会强制此服务为瞬态(每次请求时都应注入新实例),而应用程序则不应该关注。这会移动对Composition Root(应用程序的启动路径)中的生命周期服务的控制和决定,这会妨碍可维护性。
相反,请使用工厂:
public interface IBucketActionsHandlerFactory
{
IBucketActionsHandler Create(
Bucket largeBucket,
Bucket smallBucket,
int amountToRetrieve);
}
您可以将此工厂注入需要它的服务中,并让该服务提供正确的上下文变量:
public class SomeService
{
private IBucketActionsHandlerFactory factory;
private IBucketRepository repository;
public SomeService(IBucketActionsHandlerFactory factory,
IBucketRepository repository)
{
this.factory = factory;
this.repository = repository;
}
public void Handle(int amountToRetrieve)
{
var largeBucket = this.repository.GetById(LargeBucketId);
var smallBucket = this.repository.GetById(SmallBucketId);
var handler = this.factory.Create(largeBucket, smallBucket,
amountToRetrieve);
handler.CalculateSteps();
}
}
工厂将控制创建新的IBucketActionsHandler
实施:
public class BucketActionsHandlerFactory
: IBucketActionsHandlerFactory
{
private Container container;
public class BucketActionsHandlerFactory(
Container container)
{
this.container = container;
}
public IBucketActionsHandler Create(
Bucket largeBucket, Bucket smallBucket,
int amountToRetrieve)
{
return new BucketActionsHandler(
largeBucket, smallBucket, amountToRetrieve,
this.container.Get<IActionLogger>());
}
}
您的BucketActionsHandlerFactory
应该是Composition Root的一部分,在这种情况下,可以将容器/内核注入此工厂(它是DI基础结构的一部分)。
这样,应用程序不知道它获得了什么类型的处理程序,但仍然能够在其当前上下文中获得BucketActionsHandler
。
或者,您可以向largeBucket
方法提供smallBucket
,amountToRetrieve
和CalculateSteps
变量。这样您就可以不再需要工厂了:
public class BucketActionsContext
{
public Bucket LargeBucket { get; set; }
public Bucket SmallBucket { get; set; }
public int AmountToRetrieve { get; set; }
}
public class BucketActionsHandler : IBucketActionsHandler
{
private IActionLogger _actionLogger;
public BucketActionsHandler(IActionLogger actionLogger)
{
_actionLogger = actionLogger;
}
public void CalculateSteps(
BucketActionsContext context)
{
IAction nextAction;
do
{
nextAction = this.GetNextAction(context);
if (nextAction == null)
{
throw new InvalidOperationException(
"No valid action available");
}
nextAction.Execute();
}
while(!(nextAction is LastAction));
}
private IAction GetNextAction(
BucketActionsContext context)
{
return (
from action in this.GetActions(context)
where action.SatisfiedCondition()
select action)
.FirstOrDefault();
}
private IEnumerable<IAction> GetActions(
BucketActionsContext context)
{
Bucket largeBucket = context.LargeBucket;
Bucket smallBucket = context.SmallBucket;
int amountToRetrieve = context.AmountToRetrieve;
yield return new LastAction(largeBucket, smallBucket, amountToRetrieve);
yield return new EmptySmallerBucketAction(largeBucket, smallBucket, amountToRetrieve);
yield return new EmptyLargeBucketAction(largeBucket, smallBucket, amountToRetrieve);
yield return new FillLargeBucketAction(largeBucket, smallBucket, amountToRetrieve);
yield return new FillSmallBucketAction(largeBucket, smallBucket, amountToRetrieve);
yield return new TransferToLargeBucketAction(largeBucket, smallBucket, amountToRetrieve);
yield return new TransferToSmallBucketAction(largeBucket, smallBucket, amountToRetrieve);
}
}
答案 1 :(得分:0)
您可以手动解析IActionLogger
并手动将其注入构造函数,同时适当地传递其他参数
或
你可以在解析BucketActionsHandler
时进行依赖性覆盖(在Unity中调用它的方式),这会强制它将传递的值用作注入的依赖项。