假设定义了两个简单的装饰器:
// decorated object
class Product : IComponent {
// properties..
// IComponent implementation
public Decimal GetCost() {
return this.SelectedQuantity * this.PricePerPiece;
}
}
// decorators
class FixedDiscountDecorator : IComponent {
IComponent component;
// IComponent implemantation
public Decimal GetCost() {
// ...
}
public FixedDiscountDecorator(IComponent product, Decimal discountPercentage) {
// ...
}
}
class BuyXGetYFreeDiscountDecorator : IComponent {
IComponent component;
// IComponent implemantation
public Decimal GetCost() {
// ...
}
// X - things to buy
// Y - things you get free
public BuyXGetYFreeDiscountDecorator(IComponent product, Int32 X, Int32 Y) {
// ...
}
}这些装饰器具有不同的构造函数签名(参数列表)。我在寻找一种模式来应用于建筑装饰师,就像工厂模式一样。我的意思是,我放了一根线,得到了一个装饰实例。
因此,我想简单地将一系列装饰器应用于给定的产品:
var product = new SimpleProduct {
Id = Guid.NewGuid(),
PricePerPiece = 10M,
SelectedQuantity = 10,
Title = "simple product"
};
var itemsToApplyTheDiscount = 5;
var itemsYouGetFree = 2;
var discountPercentage = 0.3M;
var discountA = new BuyXGetYFreeDecorator(product, itemsToApplyTheDiscount, itemsYouGetFree);
var discountB = new FixedDiscountDecorator(discountA, discountPercentage);发布于 2013-09-03 11:31:52
可以使用IOC容器或类似的方法来解决这个问题。我脑海中浮现的一些容器是Unity、Windsor和SimpleInjector。我将把国际奥委会的容器留给其他的答案,因为我在那里没有经验。
但是,我真的很想知道为什么要注入一个本地值。
考虑到类将如何使用,在构造函数中注入诸如折扣百分比或x购买y空闲项这样的注入值会让人感到奇怪。
如果用户将10 (作为百分比)而不是0.1 (作为小数点)作为折扣参数怎么办?它会造成歧义。此外,如果在构造函数中添加检查,则会将另一个责任交给类,这违反了SRP。
我建议添加一个DTO,如DiscountPercentValue或BuyXGetYFreeValue。此外,我更喜欢将折扣的值设置为上下文,或者为其注入一个存储库。否则,总有一天你将需要factories来处理与折扣相关的if-否则业务规则。
EDIT1:
通常,我只将构造函数验证保留为null检查。除该can be以外的其他验证都被认为是违反的。
至于存储库方面,我设想了如下一些接口:
public interface IDiscountPercentageProvider{
DiscountValue Get();
}
public interface IBuyXGetYFreeValueProvider{
BuyXGetYFreeValue Get();
}然后,在您的服务类中,您可以使用如下内容:
class FixedDiscountDecorator : IComponent {
IComponent component;
// IComponent implemantation
IDiscountPercentageProvider discountPercentageProvider;
public Decimal GetCost() {
DiscountValue discount = discountPercentageProvider.Get();
// ...
}
public FixedDiscountDecorator(IComponent product
, IDiscountPercentageProvider discountPercentageProvider) {
// ... just null checks here
}
}一开始这可能很复杂。但是,它提供了更好的API设计(现在使用装饰器时没有歧义)。使用它,您可以创建一个DiscountValue作为一个保护它的不变量类,这样在其他类中使用它就更安全了。
发布于 2013-09-11 00:24:52
在您的示例中,您确实展示了应用于给定产品的一系列装饰器,即:
var discountA = new BuyXGetYFreeDecorator(product, itemsToApplyTheDiscount, itemsYouGetFree);
var discountB = new FixedDiscountDecorator(discountA, discountPercentage);那么,有哪些模式可以用来改变由指定属性所控制的product的状态,从上面的代码构建?使用您的示例,并将我的范围限制在确定product成本上:
public interface IComponent
{
decimal GetCost { get; set; }
}我将创建一个表示IComponent的产品类
public class Product
{
public IComponent Price { get; set; }
}除了装饰器类之外,您可能还有一个“默认”实现。
public class BasePrice : IComponent
{
private Decimal _cost;
public decimal GetCost //as a property maybe use Cost with get; set; in IComponent
{
get { return _cost; }
set { _cost = value; }
}
}我喜欢使用IoC并实现来自IComponent的成本变化(GetCost())的两个装饰类。
到目前为止,我并没有真正添加任何东西,只是一个基价类。接下来我可能要做的是使用一个抽象类,并将特定的操作推迟到子类,如模板方法模式中所示。我的具体类将继承这个基类。创建的具体类将取决于传递到Factory方法模式的操作类型,下面称为WarrantyProcessFactory的类。
你提到用app.config ..。我喜欢枚举,我将使用枚举来指定要应用于product的操作类型。因此,假设我想让行动与产品的保修相联系,这样我就可以在此基础上处理产品。
public enum WarrantyAction
{
RefundProduct = 0,
ReplaceProduct = 1
}我将使用保修请求类来代表客户对有缺陷产品的赔偿请求。
public class WarrantyRequest
{
public WarrantyAction Action { get; set; }
public string PaymentTransactionId { get; set; }
public decimal PricePaid { get; set; }
public decimal PostageCost { get; set; }
public long ProductId { get; set; }
public decimal AmountToRefund { get; set; }
}最后,我可以实现抽象模板方法,该方法将被表示保修动作枚举的具体类覆盖。
public abstract class WarrantyProcessTemplate
{
protected abstract void GenerateWarrantyTransactionFor(WarrantyRequest warrantyRequest);
protected abstract void CalculateRefundFor(WarrantyRequest warrantyRequest);
public void Process(WarrantyRequest warrantyRequest)
{
GenerateWarrantyTransactionFor(warrantyRequest);
CalculateRefundFor(warrantyRequest);
}
}类和前两个方法是抽象的,需要由子类实现。第三个方法只是在两个抽象方法中调用,并将一个WarrantyRequest实体作为参数传递。
客户要求退款的案例
public class RefundWarrantyProcess : WarrantyProcessTemplate
{
protected override void GenerateWarrantyTransactionFor(WarrantyRequest warrantyRequest)
{
// Code to determine terms of the warranty and devalutionAmt...
}
protected override void CalculateRefundFor(WarrantyRequest warrantyRequest)
{
WarrantyRequest.AmountToRefund = warrantyRequest.PricePaid * devalutionAmt;
}
}客户想要替换产品的情况
public class ReplaceWarrantyProcess : WarrantyProcessTemplate
{
protected override void GenerateWarrantyTransactionFor(WarrantyRequest warrantyRequest)
{
// Code to generate replacement order
}
protected override void CalculateRefundFor(WarrantyRequest warrantyRequest)
{
WarrantyRequest.AmountToRefund = warrantyRequest.PostageCost;
}
}
public static class WarrantyProcessFactory
{
public static WarrantyProcessTemplate CreateFrom(WarrantyAction warrantyAction)
{
switch (warrantyAction)
{
case (WarrantyAction.RefundProduct):
return new RefundWarrantyProcess();
case (WarrantyAction.ReplaceProduct):
return new ReplaceWarrantyProcess();
default:
throw new ApplicationException(
"No Process Template defined for Warranty Action of " +
warrantyAction.ToString());
}
}
}我要考虑的另一个模式是策略方法模式。在这种情况下,上下文类将所有计算提交给它的抽象类或接口引用的"ConcreteStrategy“。我发现Scott的书“专业ASP.NET设计模式”是一种有用的资源,并且经常回到它。
我愿意接受评论和批评。
https://stackoverflow.com/questions/18589866
复制相似问题