2017-11-10 15 views
3

良いと思われる問題を処理するには、良い方法を見つけるのに苦労しています。クリエイティブパターンの提案

私のモデル全体は価格戦略の概念に依存しています。これらの戦略は、ルールに基づいて特定の価格を計算します。私は現在、それらのそれぞれが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) { } 
} 
を実装する自体RuledBasedPricingStrategyから継承し、6つの戦略を持っているだけでどのように与えられた時間帯の料金を計算するには(12AMコストに午前8時あなたがお金の量...)

ルール

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引数として取ります。私の最初のポイントは、これらの引数を2回(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); 

ようなことをやってしまうことはできませんか?

Thxを セブ

答えて

0

あなたが述べてきたように、あなたのコンストラクタにあなたのパラメータを渡すことは、あなたのIPricingStrategiesたびに、これらのパラメータの変化の一つのそれぞれの新しいインスタンスを作成する必要がありますことを意味します。

しかし、は、戦略の2つの方法に同じパラメータセットを渡したくありません。

最初は別々の理由がありますか? Buildを呼び出すかどうかを決めることを除いて、発信者は決してIsEligibleに電話をかけたいとは思わないようです。我々は戦略にその決定を移動し、(複合)の結果を返すことができます: -

// 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); 
+0

私はそれを見逃しているどのように来ます!それははっきりしているThx – Seb