依赖注入的策略模式?

发布于 2025-02-12 09:49:08 字数 774 浏览 2 评论 0原文

是否有一种优雅的方法来处理策略模式的依赖

注入

container.Register<IMakeCoffee, MakeLatte>();
container.Register<IMakeCoffee, MakeEspresso>();
container.Register<IMakeCoffee, MakeCappuccino>();

?在这些策略中,我将其注入我的构造函数:

public Barista(IEnumerable<IMakeCoffee> coffeeStrategies) {}

但是一旦我列出了所有策略,我怎么能说我想要 ?我现在正在做的是迫使imakecoffee的每个实例具有一个咖啡馆枚举,我可以键入该枚举,以确定策略是什么类型。

public Coffee MakeCoffee(CoffeeType coffeeType)
{
    var strat = coffeeStrategies.FirstOrDefault(s => s.CoffeeType == coffeeType);
    return strat.MakeCoffee();
}

是否有一种更优雅的方式来唯一确定策略?枚举业务似乎很麻烦。

作为参考,我将Prism用于Xamarin表单应用程序,但我觉得这个问题是独立的。

Is there an elegant way to deal with dependency injection for the strategy pattern?

For instance, I have my dependency injector that I register a few different strategies with, like so:

container.Register<IMakeCoffee, MakeLatte>();
container.Register<IMakeCoffee, MakeEspresso>();
container.Register<IMakeCoffee, MakeCappuccino>();

And then when I want to access one of these strategies, I inject it into my constructor like:

public Barista(IEnumerable<IMakeCoffee> coffeeStrategies) {}

But once I have that list of all the strategies, how can I say I want this specific strategy? What I'm doing now is forcing each instance of IMakeCoffee to have a CoffeeType enum that I can key off of to determine what type the strategy is.

public Coffee MakeCoffee(CoffeeType coffeeType)
{
    var strat = coffeeStrategies.FirstOrDefault(s => s.CoffeeType == coffeeType);
    return strat.MakeCoffee();
}

Is there a more elegant way to uniquely identify a strategy? The enum business seems cumbersome.

For reference, I am using Prism for a Xamarin Forms app, but I felt this question was framework independent.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

霓裳挽歌倾城醉 2025-02-19 09:49:08

选择具有“咖啡类型”枚举的策略是有道理的,但是选择器查询在需要注意各种实施细节以进行选择的意义上是侵入性的,没有什么可以说咖啡的类型永远是任何给定策略的唯一歧视者。

我会保留详细信息,并让策略接口公开A bool Isapplicable(CoffeeMakerStrateGyContext上下文)方法,每个策略可以根据需要进行不同的实现。该方法可以将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();

Selecting a strategy with a "coffee type" enum makes sense, but the selector query is intrusive in the sense that it needs to be aware of various implementation details in order to make a selection, and nothing says the type of coffee will always ever be the only discriminator for any given strategy.

I'd keep the details encapsulated, and have the strategy interface expose a bool IsApplicable(CoffeeMakerStrategyContext context) method that each strategy can implement differently as needed. The method could take the CoffeeType enum as a parameter, but then the signature would need to change if a new strategy comes along but requires more data to make a decision; by instead passing some "strategy context" object, the signature doesn't need to change (similar to why we use EventArgs for event parameters):

public interface IStrategy<TContext>
{
    bool IsApplicable(TContext context);
}

public interface ICoffeeMakerStrategy : IStrategy<CoffeeMakerStrategyContext>
{
    Coffee MakeCoffee();
}

The selector code becomes:

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();
疏忽 2025-02-19 09:49:08

我的第一个想法是,缺乏歧视的工会使这种类型的水平更加令人沮丧。为了方便起见,要免费获得图案,我很想做类似的事情:

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 Decorator,但这有点混乱,因为链的底部必须返回无效,无论如何都会变得更加混乱。

My first thought is that a lack of discriminated unions make this more frustrating on a type level. For the convenience and to get the try get pattern for free I'd be tempted to do something like:

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(
quot;unsupported coffee type {coffeeType}");
    }

    return coffeeMaker.MakeCoffee();
}

I thought you could perhaps use DI'd decorators, but it's a bit of a mess because the bottom of the chain would have to return null, making it more messy anyway.

东风软 2025-02-19 09:49:08

您可以创建某种策略选择器:

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&lt; imakecoffee&gt;您的选择逻辑。


您的咖啡师会很高兴:

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();
}

You could create some sort of a strategy selector:

public interface IStrategySelector<TKey, TStrategy>
{
    TStrategy Select(TKey key);
}

And then you could place your selection logic into the implementation:

public sealed class CoffeeStrategySelector : IStrategySelector<CoffeeType, IMakeCoffee>
{
    public IMakeCoffee Select(CoffeeType coffeeType)
    {
        // Place your selection logic here.
    }
}

You could require your IEnumerable<IMakeCoffee> from the selector's constructor, or use any other technical way to implement your selection logic.


Your barista would be happy:

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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文