是否有一种优雅的方法来处理策略模式的依赖注入?
例如,我有我的依赖注入器,我用它注册了一些不同的策略,如下所示:
container.Register<IMakeCoffee, MakeLatte>();
container.Register<IMakeCoffee, MakeEspresso>();
container.Register<IMakeCoffee, MakeCappuccino>();然后,当我想访问这些策略之一时,我将其插入构造函数中,例如:
public Barista(IEnumerable<IMakeCoffee> coffeeStrategies) {}但是一旦我有了所有策略的清单,我怎么能说我想要这个特定的策略呢?我现在所做的是强迫IMakeCoffee的每个实例都有一个CoffeeType枚举,我可以通过这个枚举来确定策略的类型。
public Coffee MakeCoffee(CoffeeType coffeeType)
{
var strat = coffeeStrategies.FirstOrDefault(s => s.CoffeeType == coffeeType);
return strat.MakeCoffee();
}是否有一种更优雅的方法来唯一地识别策略?这件事似乎很麻烦。
作为参考,我正在使用Prism作为一个Xamarin表单应用程序,但是我觉得这个问题是框架独立的。
发布于 2022-07-01 16:15:16
选择一个具有“咖啡类型”枚举的策略是有意义的,但是选择器查询具有侵扰性,因为它需要知道各种实现细节才能做出选择,而且没有什么可以说这种咖啡类型永远是任何给定策略的唯一鉴别器。
我会对细节进行封装,并让策略接口公开一个bool IsApplicable(CoffeeMakerStrategyContext context)方法,每个策略都可以根据需要以不同的方式实现该方法。该方法可以将CoffeeType枚举作为参数,但如果出现了新策略,但需要更多数据来作出决定,则签名将需要更改;通过传递“策略上下文”对象,签名不需要更改(类似于为什么我们对事件参数使用EventArgs ):
public interface IStrategy<TContext>
{
bool IsApplicable(TContext context);
}
public interface ICoffeeMakerStrategy : IStrategy<CoffeeMakerStrategyContext>
{
Coffee MakeCoffee();
}选择器代码变成:
var context = new CoffeeMakerStrategyContext { CoffeeType = coffeeType };
var strategy = _strategies.FirstOrDefault(e => e.IsApplicable(context));
if (strategy is null)
{
throw new NotSupportedException("No applicable strategy was found for the specified context");
}
return strategy.MakeCoffee();发布于 2022-07-01 16:21:16
我的第一个想法是,缺乏受歧视的工会使得这在类型层面上更加令人沮丧。为了方便和获得免费的try get模式,我很想做这样的事情:
Dictionary<CoffeeType, IMakeCoffee> m_CoffeeMakers;
public Barista(IEnumerable<IMakeCoffee> coffeeStrategies)
{
m_CoffeeMakers = coffeeStrategies.ToDictionary(x => x.HandledCoffeeType, x => x);
}
public Coffee MakeCoffee(CoffeeType coffeeType)
{
if (!m_CoffeeMakers.TryGet(coffeeType, out var coffeeMaker)
{
throw new InvalidOperationException($"unsupported coffee type {coffeeType}");
}
return coffeeMaker.MakeCoffee();
}我认为您也许可以使用DI'd装饰器,但这有点混乱,因为链的底部必须返回null,这使得它变得更加混乱。
发布于 2022-07-01 16:31:03
您可以创建某种策略选择器:
public interface IStrategySelector<TKey, TStrategy>
{
TStrategy Select(TKey key);
}然后,您可以将选择逻辑放入实现中:
public sealed class CoffeeStrategySelector : IStrategySelector<CoffeeType, IMakeCoffee>
{
public IMakeCoffee Select(CoffeeType coffeeType)
{
// Place your selection logic here.
}
}您可以从选择器的构造函数中要求您的IEnumerable<IMakeCoffee>,或者使用任何其他技术方法来实现您的选择逻辑。
你的咖啡师会很高兴的
private readonly IStrategySelector<CoffeeType, IMakeCoffee> _coffeeSelector;
public Barista(IStrategySelector<CoffeeType, IMakeCoffee> coffeeSelector)
{
_coffeeSelector = coffeeSelector;
}
public Coffee MakeCoffee(CoffeeType coffeeType)
{
IMakeCoffee strat = _coffeeSelector.Select(coffeeType);
return strat.MakeCoffee();
}https://stackoverflow.com/questions/72831784
复制相似问题