本地购物车的实现分析
本文最后更新于 2021年4月4日 晚上
在公司遇到要实现一个本地购物车的需求, 经常都说购物车实现非常简单, 实际上有时状态一多就略显蛋疼了. 好在有状态模式, 以及在 Head First 上看到的分析方法, 才完成这个购物车的实现, 现将过程记录.
需求描述
在客户端中实现一个本地购物车, 本地购物车需要有如下功能:
用户可以在程序中多个位置利用”添加商品”按钮将商品加入到购物车中, 每点击一次商品对应的”添加商品”按钮, 购物车中就添加一个该商品(如果商品存在, 则将其选择数量加 1).
用户可以点击”购物车”按钮进入购物车界面
用户可以在购物车界面中移除单个商品
用户可以在购物车界面中移除全部商品
用户可以在购物车界面中勾选(或全选)商品
用户可以在购物车界面中反选(或全不选)商品
用户可以在购物车界面中修改任意商品当前购买数量
需要限制用户购买数量最小是 1, 最大不超过库存数量或最大可购买数量.
若用户勾选了某个(或某些)商品, 且其他商品中存在和当前已勾选商品互斥的商品(不能同时购买的商品), 则用户不能在界面中勾选这些互斥商品(勾选按钮不可用).
若用户选择的商品中存在互斥商品(不能同时购买的商品), 则不能全选.
若用户反选了某个商品, 需要重新计算当前选择集中的互斥商品列表, 并刷新不能勾选的互斥商品的列表, 从而刷新界面内商品的可选择状态.
需要在购物车界面中显示当前用户勾选的所有商品的总价, 折扣价, 以及总价折扣价差.
如果当前购物车中所有商品可以同时购买, 则进入购物车时自动将所有商品进行一个全选操作.
如果当前购物车中有商品间存在互斥, 则进入购物车时所有的商品都是未勾选状态, 且用户无法进行全选和全反选操作.
如果用户删除了某个(或某些)商品, 且购物车全部商品中不存在相互互斥的商品后, 则可以让用户进行全选和反选操作.
可以看到, 购物车中实际上有如下几块内容:
商品条目的增加和删除
商品购买数量的修改
商品的选中状态以及是否可以被选中
已勾选商品的价格计算
下面就来看看分析的办法.
购物车状态分析
和购物车相关的状态及操作如下图所示:
可以看到, 实际上整个购物车的状态并不复杂, 有如下状态:
无商品状态(空购物车)
只有一个商品: 剥离出来这个状态是为了更好地处理有无互斥商品的情况, 只有一个商品时, 是不可能存在互斥商品条件的, 且购物车需要在有互斥和无互斥状态下进行切换.
有大于一个商品且商品中无互斥商品
有大于一个商品且商品中有互斥商品
有如下可以对购物车进行的操作:
添加单个商品
移除单个商品
移除全部商品
针对购物车中的互斥商品判断, 独立一个互斥判断器即可, 每次操作如果需要判断互斥状态, 都利用互斥判断器来判断, 购物车中仅维护一个当前选择的商品条目及对应数量的列表即可.
限制商品的最小购买数量是 1, 也简化了一些操作, 这样的话商品数量的改变就不会引起购物车状态改变了, 购买数量设置操作主要有如下:
增加购买数量 1
减少购买数量 1
设置购买数量(最少 1 个, 最大不超过库存或允许购买的最大值)
上述就可以支撑起购物车的整个骨架了, 但还有一个大的方面, 就是购物车中的商品勾选, 勾选互斥, 以及勾选价格计算.
商品勾选处理
商品的勾选管理最好是独立出来, 这样能够保证职责划分更清晰, 且实现更简单.
商品互斥条件计算
在商品中携带了 ID, 通过它即可查询商品数据源中对应的商品, 进而获知该商品的互斥类别, 互斥规则是在程序启动时从服务器获取到的, 通过互斥规则和已有的商品互斥类别, 就可以计算出购物车中是否存在互斥商品, 以及和当前已勾选商品互斥的其他商品列表.
互斥也独立出一个互斥判断器来实现.
商品价格计算
商品中携带的 ID 查询出对应商品, 通过商品的类别以及购买的数量, 即可计算出商品总价, 这里也通过一个独立的价格计算器去实现.
组装成一个完整的购物车实现
外界只能够通过购物车提供的接口来访问特定功能, 所以需要把上述内容都组装起来, 组装的载体就是购物车类. 所有内容如下所示:
购物车接口
购物车类
勾选管理器类
互斥判断器类
价格计算器类
特别需要注意: 由于将勾选管理独立出来了, 如果购物车中的某个商品被删除, 则需要先在勾选管理器中将对应商品执行反选操作, 这样才能保证结果的一致.
实践出真知
如果要检验这样的设计是否合理, 最好的办法就是编写出来看看, 特别是要检验一下把勾选管理独立出来的合理性.