创意模式建议
Creational Patterns suggestion
我正在努力寻找一个好的方法来处理一个看起来不错的问题。
我的整个模型都依赖于定价策略的概念。这些策略根据规则计算特定价格。规定了如何计算给定时间范围内的价格(早上 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);
}
定价很简单class,如下所示:
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)。然而,我需要构建一个 "factory" 来了解所有策略类型并在每次收到新请求时实例化它们:
public IEnumerable<IPricingStrategy> Find(Booking booking, Resource resource, TecTacClient client)
=> _strategiesType
.Select(t => Activator.CreateInstance(t, booking, resource, client))
.OfType<IPricingStrategy>();
这似乎不是正确的方法。 Strategy应该被实例化一次,然后重新用于计算strategy。但是我不能在那种情况下我最终会做类似
的事情
foreach(var s in strategies)
if (s.IsEligible(booking, resource, client))
yield return s.Build(booking, resource, client);
我可以使用哪些模式来 simplify/clean 此代码?
谢谢
塞布
正如您所指出的,将参数传递给构造函数意味着每次这些参数之一发生变化时,您都需要为每个 IPricingStrategies
创建一个新实例。
但是,您不想将同一组参数传递给策略的两个方法。
这两种方法首先是分开的,这有充分的理由吗?在我看来,调用者永远不会想要调用 IsEligible
,除非决定是否调用 Build
。我们可以将该决定移至战略中,return(综合)结果:-
// 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);
我正在努力寻找一个好的方法来处理一个看起来不错的问题。
我的整个模型都依赖于定价策略的概念。这些策略根据规则计算特定价格。规定了如何计算给定时间范围内的价格(早上 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);
}
定价很简单class,如下所示:
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)。然而,我需要构建一个 "factory" 来了解所有策略类型并在每次收到新请求时实例化它们:
public IEnumerable<IPricingStrategy> Find(Booking booking, Resource resource, TecTacClient client)
=> _strategiesType
.Select(t => Activator.CreateInstance(t, booking, resource, client))
.OfType<IPricingStrategy>();
这似乎不是正确的方法。 Strategy应该被实例化一次,然后重新用于计算strategy。但是我不能在那种情况下我最终会做类似
的事情foreach(var s in strategies)
if (s.IsEligible(booking, resource, client))
yield return s.Build(booking, resource, client);
我可以使用哪些模式来 simplify/clean 此代码?
谢谢 塞布
正如您所指出的,将参数传递给构造函数意味着每次这些参数之一发生变化时,您都需要为每个 IPricingStrategies
创建一个新实例。
但是,您不想将同一组参数传递给策略的两个方法。
这两种方法首先是分开的,这有充分的理由吗?在我看来,调用者永远不会想要调用 IsEligible
,除非决定是否调用 Build
。我们可以将该决定移至战略中,return(综合)结果:-
// 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);