如何优雅的设计购物车
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何优雅的设计购物车相关的知识,希望对你有一定的参考价值。
转载请注明出处,感谢您的阅读。 http://www.cnblogs.com/HeroWong/
要点:
1. 购物车通常分为登录和不登录,登录存储在数据库中,未登录存储到Session/Cookie中。
2. Mvc中有个很好的特性:自定义模型绑定,开发者可以在Action方法的参数中添加一个复杂对象,由Mvc自动装配上这个对象。通过这个我们就能轻易的运用多态,屏蔽SessionCart和DbCart对象之间的差异。
3. “添加商品”和“删除商品”两个看似简单的方法,如何优雅的设计?
Part 1,一个基本的购物车(对购物车有自己独特的见解可以直接看Part2了。Part1会有很多冗余代码,Part2是进行重构。)
CartLine实体
public class CartLine { public Guid Id { get; set; } public int GoodsId { get; set; } public int Quantity { get; set; } public int UserId { get; set; } public DateTime AddTime { get; set; } public Goods Goods { get; set; } public CartLine() { Id = Guid.NewGuid(); AddTime = DateTime.Now; } }
购物车抽象类
public abstract class Cart { public abstract void Add(Goods goods, int quantity); public abstract void Remove(Goods goods, int quantity); public abstract void RemoveLine(int goodsId); }
SessionCart
public class SessionCart : Cart { readonly List<CartLine> lines; public SessionCart(List<CartLine> lines) { this.lines = lines; } public override void Add(Goods goods, int quantity) { if (quantity < 1) { throw new ArgumentException("quantity值不能小于\\"1\\"", "quantity"); } var line = lines.SingleOrDefault(c => c.GoodsId == goods.Id); if (line == null) { lines.Add(new CartLine { GoodsId = goods.Id, Goods = goods, Quantity = quantity }); } else { line.Quantity += quantity; } } public override void Remove(Goods goods, int quantity) { if (quantity < 1) { throw new ArgumentException("quantity值不能小于\\"1\\"", "quantity"); } var line = lines.SingleOrDefault(c => c.GoodsId == goods.Id); if (line != null) { if (line.Quantity >= quantity) { RemoveLine(quantity); } else { line.Quantity -= quantity; } } } public override void RemoveLine(int goodsId) { var line = lines.SingleOrDefault(c => c.GoodsId == goodsId); lines.Remove(line); } }
DbCart
public class DbCart : Cart { readonly HappyDogContext context; public DbCart(int userId, HappyDogContext context) { this.context = context; UserId = userId; } public int UserId { get; private set; } public override void Add(Goods goods, int quantity) { if (quantity < 1) { throw new ArgumentException("quantity值不能小于\\"1\\"", "quantity"); } var line = context.CartLines.SingleOrDefault(c => c.GoodsId == goods.Id && c.UserId == UserId); if (line == null) { context.CartLines.Add(new CartLine { GoodsId = goods.Id, Quantity = quantity, UserId = UserId }); } else { line.Quantity += quantity; } context.SaveChanges(); } public override void Remove(Goods goods, int quantity) { if (quantity < 1) { throw new ArgumentException("quantity值不能小于\\"1\\"", "quantity"); } var line = context.CartLines.SingleOrDefault(c => c.GoodsId == goods.Id && c.UserId == UserId); if (line != null) { if (line.Quantity >= quantity) { RemoveLine(quantity); } else { line.Quantity -= quantity; } context.SaveChanges(); } } public override void RemoveLine(int goodsId) { var line = context.CartLines.SingleOrDefault(c => c.GoodsId == goodsId && c.UserId == UserId); context.CartLines.Remove(line); } }
以上就是一个普通的,不够优雅的购物车。我们不难发现,出现了很多冗余代码
问题如下:
1.查找一个CartLine冗余
2.判断quantity值是否合理冗余
3.对于后续扩展不够友好,例如 如果再加入CookieCart,还需要实现太多方法。
Part 2
重构分析:我们发现,SessionCart和DbCart里面的Add/Remove两个方法,代码太过类似,能否复用?(当然可以)
当我们发现子类有重复的代码,可以考虑把这些重复的放到父类(模板方法模式)。
重构后的Cart
public abstract class Cart { public abstract void RemoveLine(int goodsId); public abstract CartLine this[int goodsId] { get; } protected abstract void RealAdd(Goods goods, int quantity); protected abstract void SaveChanges(); public void Add(Goods goods, int quantity) { if (quantity < 1) { throw new ArgumentException("quantity值不能小于\\"1\\"", "quantity"); } var line = this[goods.Id]; if (line == null) { RealAdd(goods, quantity); } else { line.Quantity += quantity; } SaveChanges(); } public void Remove(Goods goods, int quantity) { if (quantity < 1) { throw new ArgumentException("quantity值不能小于\\"1\\"", "quantity"); } var line = this[goods.Id]; if (line != null) { if (line.Quantity >= quantity) { RemoveLine(goods.Id); } else { line.Quantity -= quantity; } SaveChanges(); } } }
重构后的SessionCart
public class SessionCart : Cart { readonly List<CartLine> lines; public SessionCart(List<CartLine> lines) { this.lines = lines; } public override CartLine this[int goodsId] => lines.SingleOrDefault(c => c.GoodsId == goodsId); public override void RemoveLine(int goodsId) => lines.Remove(this[goodsId]); protected override void RealAdd(Goods goods, int quantity) { lines.Add(new CartLine { GoodsId = goods.Id, Quantity = quantity }); } protected override void SaveChanges() { } }
重构后的DbCart
public class DbCart : Cart { readonly HappyDogContext context; public DbCart(int userId, HappyDogContext context) { this.context = context; UserId = userId; } public int UserId { get; private set; } public override CartLine this[int goodsId] { get => context.CartLines.SingleOrDefault(c => c.GoodsId == goodsId && c.UserId == UserId); } protected override void RealAdd(Goods goods, int quantity) { context.CartLines.Add(new CartLine { GoodsId = goods.Id, Quantity = quantity, UserId = UserId }); } protected override void SaveChanges() => context.SaveChanges(); public override void RemoveLine(int goodsId) { var line = this[goodsId]; if (line != null) { context.CartLines.Remove(line); context.SaveChanges(); } } }
最后一段是Mvc的自定义模型绑定
namespace HappyDog.Web.WebUI.Binders { public class CartBinder : IModelBinder { const string sessionKey = "Cart"; public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { HappyDogContext context = new HappyDogContext(); ICart cart; var identity = controllerContext.HttpContext.User.Identity; if (identity.IsAuthenticated) { cart = new DbCart(int.Parse(identity.Name), context); } else { cart = (SessionCart)controllerContext.HttpContext.Session[sessionKey]; if (cart==null) { cart = new SessionCart(context); controllerContext.HttpContext.Session[sessionKey] = cart; } } return cart; } } }
在Global中注册下上述。
调用: public ViewResult Add(Cart cart,Goods goods){ }
希望对大家有帮助。如果不足之处希望您能够指点。
以上是关于如何优雅的设计购物车的主要内容,如果未能解决你的问题,请参考以下文章
如何从 recyclerview 片段传递到另一个 recyclerview 片段
在编写RTOS代码时,如何设计一个简单优雅可拓展的任务初始化结构?