C# 购物车和订单中的折扣策略
我正在尝试实现一个系统,可以处理应用于我的购物车/已完成订单的多个折扣。我已经应用了一个策略类型模式来封装折扣中折扣的处理 我提出了以下内容:一个抽象的折扣基类,其子类构成了具体的折扣。然后将这些应用于订单/购物车对象,并在添加到购物车/订单时处理订单/购物车的内容 希望对所附代码有一些评论。nhibernate需要各种标记为“虚拟”的受保护构造函数和成员 切夫C# 购物车和订单中的折扣策略,c#,design-patterns,architecture,e-commerce,C#,Design Patterns,Architecture,E Commerce,我正在尝试实现一个系统,可以处理应用于我的购物车/已完成订单的多个折扣。我已经应用了一个策略类型模式来封装折扣中折扣的处理 我提出了以下内容:一个抽象的折扣基类,其子类构成了具体的折扣。然后将这些应用于订单/购物车对象,并在添加到购物车/订单时处理订单/购物车的内容 希望对所附代码有一些评论。nhibernate需要各种标记为“虚拟”的受保护构造函数和成员 切夫 使用系统; 使用System.Collections.Generic; 使用System.Linq; 使用NUnit.Framewor
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用NUnit.Framework;
命名空间CodeCollective.RaceFace.DispentEngine
{
[测试夹具]
公共类测试
{
#区域测试
[测试]
公共作废可以添加物品到购物车()
{
Cart=LoadCart();
//显示购物车内容
foreach(购物车中的LineItem LineItem.LineItems)
{
Console.WriteLine(“产品:{0}\t价格:{1:c}\t数量:{2}\t小计:{4:c}\t折扣:{3:c}\t|折扣应用:{5}”、lineItem.Product.Name、lineItem.Product.Price、lineItem.Quantity、lineItem.deffentamount、lineItem.Subtotal、lineItem.defficies.Count);
}
}
[测试]
public void可以将项目添加到订单()中
{
//创建购物车
订单=新订单(新成员(“Chev”);
//将项目添加到购物车
GenericProduct hat=新的GenericProduct(“Cap”,110米);
订单。添加行项目(hat,5);
EventItem比赛=新的EventItem(“票子”,90米);
order.AddLineItem(比赛,1);
//增加折扣
折扣百分比折扣=新的折扣百分比(“所有项目10%折扣”,0.10m);
percentageOff.canbeusedinjunitionwithother折扣=false;
订单。添加折扣(百分比折扣);
折扣支出xGety=新支出超过xGetyDiscount(“支出超过100兰特可享受10%折扣”,1亿兰特,0.10万兰特);
spendXgetY.supercedesotherdiscounters=true;
订单。添加折扣(spendXgetY);
折扣buyXGetY=new BuyXGetYFree(“买4顶帽子,送2顶帽子”,新列表{hat},4,2);
buyXGetY.canbeusedinjunition with other折扣=false;
buyXGetY.supercedesotherdiscounters=true;
订单。添加折扣(购买折扣);
//显示购物车内容
foreach(按顺序排列的行项目。行项目)
{
Console.WriteLine(“产品:{0}\t价格:{1:c}\t数量:{2}\t小计:{4:c}\t折扣:{3:c}\t|折扣应用:{5}”、lineItem.Product.Name、lineItem.Product.Price、lineItem.Quantity、lineItem.deffentamount、lineItem.Subtotal、lineItem.defficies.Count);
}
}
[测试]
public void可以将购物车处理为订单()
{
Cart=LoadCart();
订单订单=ProcessCartToOrder(购物车);
//显示购物车内容
foreach(按顺序排列的行项目。行项目)
{
Console.WriteLine(“产品:{0}\t价格:{1:c}\t数量:{2}\t小计:{4:c}\t折扣:{3:c}\t|折扣应用:{5}”、lineItem.Product.Name、lineItem.Product.Price、lineItem.Quantity、lineItem.deffentamount、lineItem.Subtotal、lineItem.defficies.Count);
}
}
私有静态购物车LoadCart()
{
//创建购物车
购物车=新购物车(新会员(“Chev”));
//将项目添加到购物车
GenericProduct hat=新的GenericProduct(“Cap”,110米);
cart.AddLineItem(帽子,5);
EventItem比赛=新的EventItem(“票子”,90米);
cart.AddLineItem(比赛,1);
//增加折扣
折扣百分比折扣=新的折扣百分比(“所有项目10%折扣”,0.10m);
percentageOff.canbeusedinjunitionwithother折扣=false;
购物车添加折扣(百分比折扣);
折扣支出xGety=新支出超过xGetyDiscount(“支出超过100兰特可享受10%折扣”,1亿兰特,0.10万兰特);
spendXgetY.supercedesotherdiscounters=true;
购物车附加折扣(spendXgetY);
折扣buyXGetY=new BuyXGetYFree(“买4顶帽子,送2顶帽子”,新列表{hat},4,2);
buyXGetY.canbeusedinjunition with other折扣=false;
buyXGetY.supercedesotherdiscounters=true;
购物车附加折扣(buyXGetY);
返回车;
}
专用静态订单处理CartToOrder(购物车)
{
订单=新订单(购物车成员);
foreach(购物车中的LineItem LineItem.LineItems)
{
order.AddLineItem(lineItem.Product,lineItem.Quantity);
foreach(行项目中的折扣。折扣)
{
订单。添加折扣(折扣);
}
}
退货单;
}
#端区
}
#地区折扣
[可序列化]
公共抽象类折扣:EntityBase
{
受保护的内部折扣()
{
}
公共折扣(字符串名称)
{
名称=名称;
}
公共虚拟bool可以在具有其他折扣的六月使用{get;set;}
公共虚拟布尔值取代其他折扣{get;set;}
公共抽象OrderBase ApplyDiscount();
公共虚拟医嘱库医嘱库{get;set;}
公共虚拟字符串名称{get;private set;}
}
[可序列化]
公共类百分比折扣:折扣
{
受保护的内部百分比折扣()
{
}
公共折扣百分比(字符串名称,十进制折扣百分比)
:base(名称)
{
折扣百分比=折扣百分比;
}
公共覆盖OrderBase ApplyDiscount()
{
//定制处理
foreach(OrderBase.LineItems中的LineItem LineItem)
{
lineItem.DiscountAmount=lineItem.Product.Price*折扣百分比;
lineItem.AddDiscount(此项);
}
退货订单库;
}
公共虚拟十进制折扣百分比{get;set;}
}
[可序列化]
公共类BuyXGetYFree:折扣
{
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace CodeCollective.RaceFace.DiscountEngine
{
[TestFixture]
public class TestAll
{
#region Tests
[Test]
public void Can_Add_Items_To_Cart()
{
Cart cart = LoadCart();
// display the cart contents
foreach (LineItem lineItem in cart.LineItems)
{
Console.WriteLine("Product: {0}\t Price: {1:c}\t Quantity: {2} \t Subtotal: {4:c} \t Discount: {3:c} \t| Discounts Applied: {5}", lineItem.Product.Name, lineItem.Product.Price, lineItem.Quantity, lineItem.DiscountAmount, lineItem.Subtotal, lineItem.Discounts.Count);
}
}
[Test]
public void Can_Add_Items_To_An_Order()
{
// create the cart
Order order = new Order(new Member("Chev"));
// add items to the cart
GenericProduct hat = new GenericProduct("Cap", 110m);
order.AddLineItem(hat, 5);
EventItem race = new EventItem("Ticket", 90m);
order.AddLineItem(race, 1);
// add discounts
Discount percentageOff = new PercentageOffDiscount("10% off all items", 0.10m);
percentageOff.CanBeUsedInJuntionWithOtherDiscounts = false;
order.AddDiscount(percentageOff);
Discount spendXgetY = new SpendMoreThanXGetYDiscount("Spend more than R100 get 10% off", 100m, 0.1m);
spendXgetY.SupercedesOtherDiscounts = true;
order.AddDiscount(spendXgetY);
Discount buyXGetY = new BuyXGetYFree("Buy 4 hats get 2 hat free", new List<Product> { hat }, 4, 2);
buyXGetY.CanBeUsedInJuntionWithOtherDiscounts = false;
buyXGetY.SupercedesOtherDiscounts = true;
order.AddDiscount(buyXGetY);
// display the cart contents
foreach (LineItem lineItem in order.LineItems)
{
Console.WriteLine("Product: {0}\t Price: {1:c}\t Quantity: {2} \t Subtotal: {4:c} \t Discount: {3:c} \t| Discounts Applied: {5}", lineItem.Product.Name, lineItem.Product.Price, lineItem.Quantity, lineItem.DiscountAmount, lineItem.Subtotal, lineItem.Discounts.Count);
}
}
[Test]
public void Can_Process_A_Cart_Into_An_Order()
{
Cart cart = LoadCart();
Order order = ProcessCartToOrder(cart);
// display the cart contents
foreach (LineItem lineItem in order.LineItems)
{
Console.WriteLine("Product: {0}\t Price: {1:c}\t Quantity: {2} \t Subtotal: {4:c} \t Discount: {3:c} \t| Discounts Applied: {5}", lineItem.Product.Name, lineItem.Product.Price, lineItem.Quantity, lineItem.DiscountAmount, lineItem.Subtotal, lineItem.Discounts.Count);
}
}
private static Cart LoadCart()
{
// create the cart
Cart cart = new Cart(new Member("Chev"));
// add items to the cart
GenericProduct hat = new GenericProduct("Cap", 110m);
cart.AddLineItem(hat, 5);
EventItem race = new EventItem("Ticket", 90m);
cart.AddLineItem(race, 1);
// add discounts
Discount percentageOff = new PercentageOffDiscount("10% off all items", 0.10m);
percentageOff.CanBeUsedInJuntionWithOtherDiscounts = false;
cart.AddDiscount(percentageOff);
Discount spendXgetY = new SpendMoreThanXGetYDiscount("Spend more than R100 get 10% off", 100m, 0.1m);
spendXgetY.SupercedesOtherDiscounts = true;
cart.AddDiscount(spendXgetY);
Discount buyXGetY = new BuyXGetYFree("Buy 4 hats get 2 hat free", new List<Product> { hat }, 4, 2);
buyXGetY.CanBeUsedInJuntionWithOtherDiscounts = false;
buyXGetY.SupercedesOtherDiscounts = true;
cart.AddDiscount(buyXGetY);
return cart;
}
private static Order ProcessCartToOrder(Cart cart)
{
Order order = new Order(cart.Member);
foreach(LineItem lineItem in cart.LineItems)
{
order.AddLineItem(lineItem.Product, lineItem.Quantity);
foreach(Discount discount in lineItem.Discounts)
{
order.AddDiscount(discount);
}
}
return order;
}
#endregion
}
#region Discounts
[Serializable]
public abstract class Discount : EntityBase
{
protected internal Discount()
{
}
public Discount(string name)
{
Name = name;
}
public virtual bool CanBeUsedInJuntionWithOtherDiscounts { get; set; }
public virtual bool SupercedesOtherDiscounts { get; set; }
public abstract OrderBase ApplyDiscount();
public virtual OrderBase OrderBase { get; set; }
public virtual string Name { get; private set; }
}
[Serializable]
public class PercentageOffDiscount : Discount
{
protected internal PercentageOffDiscount()
{
}
public PercentageOffDiscount(string name, decimal discountPercentage)
: base(name)
{
DiscountPercentage = discountPercentage;
}
public override OrderBase ApplyDiscount()
{
// custom processing
foreach (LineItem lineItem in OrderBase.LineItems)
{
lineItem.DiscountAmount = lineItem.Product.Price * DiscountPercentage;
lineItem.AddDiscount(this);
}
return OrderBase;
}
public virtual decimal DiscountPercentage { get; set; }
}
[Serializable]
public class BuyXGetYFree : Discount
{
protected internal BuyXGetYFree()
{
}
public BuyXGetYFree(string name, IList<Product> applicableProducts, int x, int y)
: base(name)
{
ApplicableProducts = applicableProducts;
X = x;
Y = y;
}
public override OrderBase ApplyDiscount()
{
// custom processing
foreach (LineItem lineItem in OrderBase.LineItems)
{
if(ApplicableProducts.Contains(lineItem.Product) && lineItem.Quantity > X)
{
lineItem.DiscountAmount += ((lineItem.Quantity / X) * Y) * lineItem.Product.Price;
lineItem.AddDiscount(this);
}
}
return OrderBase;
}
public virtual IList<Product> ApplicableProducts { get; set; }
public virtual int X { get; set; }
public virtual int Y { get; set; }
}
[Serializable]
public class SpendMoreThanXGetYDiscount : Discount
{
protected internal SpendMoreThanXGetYDiscount()
{
}
public SpendMoreThanXGetYDiscount(string name, decimal threshold, decimal discountPercentage)
: base(name)
{
Threshold = threshold;
DiscountPercentage = discountPercentage;
}
public override OrderBase ApplyDiscount()
{
// if the total for the cart/order is more than x apply discount
if(OrderBase.GrossTotal > Threshold)
{
// custom processing
foreach (LineItem lineItem in OrderBase.LineItems)
{
lineItem.DiscountAmount += lineItem.Product.Price * DiscountPercentage;
lineItem.AddDiscount(this);
}
}
return OrderBase;
}
public virtual decimal Threshold { get; set; }
public virtual decimal DiscountPercentage { get; set; }
}
#endregion
#region Order
[Serializable]
public abstract class OrderBase : EntityBase
{
private IList<LineItem> _LineItems = new List<LineItem>();
private IList<Discount> _Discounts = new List<Discount>();
protected internal OrderBase() { }
protected OrderBase(Member member)
{
Member = member;
DateCreated = DateTime.Now;
}
public virtual Member Member { get; set; }
public LineItem AddLineItem(Product product, int quantity)
{
LineItem lineItem = new LineItem(this, product, quantity);
_LineItems.Add(lineItem);
return lineItem;
}
public void AddDiscount(Discount discount)
{
discount.OrderBase = this;
discount.ApplyDiscount();
_Discounts.Add(discount);
}
public virtual decimal GrossTotal
{
get
{
return LineItems
.Sum(x => x.Product.Price * x.Quantity);
}
}
public virtual DateTime DateCreated { get; private set; }
public IList<LineItem> LineItems
{
get
{
return _LineItems;
}
}
}
[Serializable]
public class Order : OrderBase
{
protected internal Order() { }
public Order(Member member)
: base(member)
{
}
}
#endregion
#region LineItems
[Serializable]
public class LineItem : EntityBase
{
private IList<Discount> _Discounts = new List<Discount>();
protected internal LineItem() { }
public LineItem(OrderBase order, Product product, int quantity)
{
Order = order;
Product = product;
Quantity = quantity;
}
public virtual void AddDiscount(Discount discount)
{
_Discounts.Add(discount);
}
public virtual OrderBase Order { get; private set; }
public virtual Product Product { get; private set; }
public virtual int Quantity { get; private set; }
public virtual decimal DiscountAmount { get; set; }
public virtual decimal Subtotal
{
get { return (Product.Price*Quantity) - DiscountAmount; }
}
public virtual IList<Discount> Discounts
{
get { return _Discounts.ToList().AsReadOnly(); }
}
}
#endregion
#region Member
[Serializable]
public class Member : EntityBase
{
protected internal Member() { }
public Member(string name)
{
Name = name;
}
public virtual string Name { get; set; }
}
#endregion
#region Cart
[Serializable]
public class Cart : OrderBase
{
protected internal Cart()
{
}
public Cart(Member member)
: base(member)
{
}
}
#endregion
#region Products
[Serializable]
public abstract class Product : EntityBase
{
protected internal Product()
{
}
public Product(string name, decimal price)
{
Name = name;
Price = price;
}
public virtual string Name { get; set; }
public virtual decimal Price { get; set; }
}
// generic product used in most situations for simple products
[Serializable]
public class GenericProduct : Product
{
protected internal GenericProduct()
{
}
public GenericProduct(String name, Decimal price) : base(name, price)
{
}
}
// custom product with additional properties and methods
[Serializable]
public class EventItem : Product
{
protected internal EventItem()
{
}
public EventItem(string name, decimal price) : base(name, price)
{
}
}
#endregion
#region EntityBase
[Serializable]
public abstract class EntityBase
{
private readonly Guid _id;
protected EntityBase() : this(GenerateGuidComb())
{
}
protected EntityBase(Guid id)
{
_id = id;
}
public virtual Guid Id
{
get { return _id; }
}
private static Guid GenerateGuidComb()
{
var destinationArray = Guid.NewGuid().ToByteArray();
var time = new DateTime(0x76c, 1, 1);
var now = DateTime.Now;
var span = new TimeSpan(now.Ticks - time.Ticks);
var timeOfDay = now.TimeOfDay;
var bytes = BitConverter.GetBytes(span.Days);
var array = BitConverter.GetBytes((long)(timeOfDay.TotalMilliseconds / 3.333333));
Array.Reverse(bytes);
Array.Reverse(array);
Array.Copy(bytes, bytes.Length - 2, destinationArray, destinationArray.Length - 6, 2);
Array.Copy(array, array.Length - 4, destinationArray, destinationArray.Length - 4, 4);
return new Guid(destinationArray);
}
public virtual int Version { get; protected set; }
#region Equality Tests
public override bool Equals(object entity)
{
return entity != null
&& entity is EntityBase
&& this == (EntityBase)entity;
}
public static bool operator ==(EntityBase base1,
EntityBase base2)
{
// check for both null (cast to object or recursive loop)
if ((object)base1 == null && (object)base2 == null)
{
return true;
}
// check for either of them == to null
if ((object)base1 == null || (object)base2 == null)
{
return false;
}
if (base1.Id != base2.Id)
{
return false;
}
return true;
}
public static bool operator !=(EntityBase base1, EntityBase base2)
{
return (!(base1 == base2));
}
public override int GetHashCode()
{
{
return Id.GetHashCode();
}
}
#endregion
#endregion
}