3

我正在努力寻找一种处理看似不错的问题的好方法。

我的整个模型依赖于定价策略的概念。这些策略根据规则计算特定价格。规则只是如何计算给定时间范围内的价格(早上 8 点到 12 点花了你这么多钱......)

我目前有 6 个策略,每个策略都继承自 RuledBasedPricingStrategy,后者本身实现了 IStrategy

public abstract class RuledBasedPricingStrategy : IPricingStrategy
{
    public abstract string FriendlyName { get; }
    protected abstract ICollection<IPricingRule> Rules { get; }

    protected Booking Booking { get; }
    protected Resource Resource { get; }
    protected TecTacClient Client { get; }

    protected RuledBasedPricingStrategy(Booking booking, Resource resource, TecTacClient client)
    { 
       // Just checking and assigning values(...) 
    }

    public abstract bool IsEligible();

    public Pricing Build()
    {
        var strategy = new Pricing(FriendlyName, Resource.CurrencyCode);
        foreach (var r in Rules.OrderByDescending(x => x.Priority))
            r.Apply(strategy, Booking, Resource, Client);
        return strategy;
    }
}

public class RegularPricingCalculatorFactory : RuledBasedPricingStrategy
{
    public override string FriendlyName => "Regular Pricing";

    protected override ICollection<IPricingRule> Rules => new List<IPricingRule>
    {
        new OfficeHourPricingRule(),
        // Non Office Hours
        new OnePercentSurchagePricingRule()
    };

    public override bool IsEligible() => (...)

    public RegularPricingCalculatorFactory(Booking booking, Resource resource, TecTacClient client)
        : base(booking, resource, client)
    { }
}

public class HalfdayPricingStrategy : RuledBasedPricingStrategy
{
    public override string FriendlyName => "Half day Pricing";

    protected override ICollection<IPricingRule> Rules => new List<IPricingRule>
    {
        new HalfDayPricingRule(),
        new OfficeHourPricingRule(),
        // Non Office Hours
    };

    public override bool IsEligible() => (...)

    public HalfdayPricingStrategy(Booking booking, Resource resource, TecTacClient client) 
        : base(booking, resource, client) { }
}

IRule 是一个简单的接口。规则接受定价作为参数,并将根据需要添加尽可能多的定价步骤:

public interface IPricingRule
{
    int Priority { get; }
    void Apply(Pricing pricing, Booking booking, Resource resource, TecTacClient client);
}

定价是一个简单的类,如下所示:

public class Pricing
{
    public string Name { get; }
    public string CurrencyCode { get; }
    private readonly List<PricingStep> _steps;
    public ICollection<PricingStep> Steps => _steps.AsReadOnly();
    public decimal TotalPrice => Steps.Sum(x => x.Price);
 }

等等 (...)

我正在寻找一种实例化所有这些策略的好方法。正如您所注意到的,它们将预订、资源和客户端对象作为 ctor 参数。我最初的观点是避免两次传递这些参数(IsEligible 和 Build)。但是,我需要建立一个“工厂”,它会知道所有策略类型并在每次收到新请求时实例化它们:

    public IEnumerable<IPricingStrategy> Find(Booking booking, Resource resource, TecTacClient client)
        => _strategiesType
            .Select(t => Activator.CreateInstance(t, booking, resource, client))
            .OfType<IPricingStrategy>();

这似乎不是正确的方法。策略应该被实例化一次,然后重新用于计算策略。但是我不能在那种情况下我最终会做类似的事情

foreach(var s in strategies)
    if (s.IsEligible(booking, resource, client))
        yield return s.Build(booking, resource, client);

我可以使用哪些模式来简化/清理此代码?

谢谢 Seb

4

1 回答 1

0

IPricingStrategies正如您所指出的,将您的参数传递给您的构造函数意味着每次这些参数之一发生更改时,您都需要为每个参数创建一个新实例。

然而,您不想将同一组参数传递给您的策略的两个方法。

这两种方法首先是分开的有充分的理由吗?在我看来,调用者永远不会想要打电话,IsEligible除非决定是否打电话Build。我们可以将该决定移至策略中,并返回(复合)结果:-

// You can make this guy more sophisticated by building a proper
// option type if you like, but this is good enough as an example.
class PricingStrategyResult
{
  public bool IsEligible { get; set; }
  public Pricing Pricing { get; set; }
}

interface IPricingStrategy
{
  // Your caller passes the parameters and receives both values
  // at once.
  PricingStrategyResult Build(
    Booking booking,
    Resource resource,
    TecTacClient client)
}

// But you can still split the logic up at the implementation
// level if you need to.
public abstract class RuledBasedPricingStrategy : IPricingStrategy
{
  protected abstract bool IsEligible();

  public PricingStrategyResult Build(
    Booking booking,
    Resource resource,
    TecTacClient client)
  {
    if (!this.IsEligible)
    {
      return new PricingStrategyResult()
      {
        IsEligible = false
      };
    }

    var pricing = new Pricing(FriendlyName, Resource.CurrencyCode);

    foreach (var r in Rules.OrderByDescending(x => x.Priority))
    {
      r.Apply(pricing, booking, resource, client);
    }

    return new PricingStrategyResult()
    {
      IsEligible = true,
      Pricing = pricing
    };
  }
}

然后你称之为: -

results = strategies.Build(booking, resource, client)
                    .Where(x => x.IsEligible)
                    .Select(x => x.Pricing);
于 2017-11-10T09:34:23.670 回答