SuperMarket系统的设计模式

时间:2017-03-30 14:39:06

标签: design-patterns point-of-sale

我是软件开发人员,我开始像软件开发人员II一样思考。作为访谈的一部分,我的任务是编写一个相对简单的编码挑战用例:构建超市定价系统。

  

规则和要求:Super Foods的每个项目都由a标识   独特的四位数代码。今天,Super Foods的定价方案使用了   以下定价类别,但要注意:价格是不断的   变化,销售部门总是在创造新的激励机制   和交易,比如买一送一。

     

EG:芯片和莎莎(第6732和#4900项)共同售价4.99美元,但是   它们分别仅花费2.49美元和3.49美元。

     

EG2:买两把牙刷,每张1.99美元,免费送一只。

     

EG3:一瓶葡萄酒(第0923项)售价15.49美元,并征税   额外9.25%

通过阅读设计模式,这看起来像某种形式的Decorator模式的自然位置,用于总计对象的销售。具有模式<ID, ObjectName, Price>的SQLite数据库在某种程度上也是有用的,尽管我对如何在所有这些中创建数据访问对象感到厌烦。

我试图以一种完整的堆栈MVC思维方式围绕这一点,我觉得我可能会生气。这是Spring Framework的名声吗?也许建议使用这个用例的更好的API?

感谢所有帮助我集体讨论该系统设计的人。

4 个答案:

答案 0 :(得分:2)

装饰器模式用于添加/修改现有类的行为,而无需更改类本身。因此,它充当现有类的包装器,因此您可能会想到它。点是您没有扩展的系统,您正在从头开始构建它!

S / W设计很难,无法一次完成。此外,我确信您的潜在客户对您的设计方式更感兴趣,而不是您使用的技术堆栈。所以我不会对此发表评论。这是你的电话。

根据您的要求,这些是我最初的想法。还有改进的余地(是的!)但至少这应该适用于你的任务范围。这是C#。这不应该阻止你理解它。

namespace Entities {
    public class StoreItem 
    {
        // Code
        // Name
        // Cost
        // Tax -> for specific items
        // MfgDate
        // ExpDate
    }

    public class StoreDeal 
    {
        // Code
        // Name
        // Cost
        // Validity
        // Items (type: IList<StoreItem>) -> items participating in a deal
    }
}

namespace Domain {
    public class Cart 
    {
        // Items (type: IList<CartItem>)
        // TotalAmount
        // TotalDiscount
        // FinalAmount
    }

    public class CartItem
    {
        public CartItem(string code) {
            Code = code; // assume "6732" -> Chips
        }

        public CartItem(StoreItem item) {
            MapStoreItem(item);
        }

        // declare props: Code, Name, Quantity, Cost

        public void Prepare() {
            if(Quantity > 0) {
                // Invalid operation alert and return
                // This is one time call per item type
            }
            // Sample. Retrieve item from database.
            var item = new StoreItem { Code = code, Name = "Chips", Cost = 2.49, Tax = 0 /* etc */ }
            MapStoreItem(item);
            Quantity = 1;
        }

        public void UpdateQuantity(int quantity) {
            Quantity = quantity;
            Cost = Cost * Quantity;
        }

        private void MapStoreItem(StoreItem item) {
            Code = item.Code;
            Name = item.Name;
            Cost = CalculateCost(item.Cost, item.Tax);
        }

        private static double CalculateCost(double cost, double tax) {
            // If tax > 0, apply it to cost
            // else return cost as is
        }
    }
}

public class DealService
{
    public StoreDeal GetDeal(string itemCode) {
        // Assume item to be Chips. Retrieve current deal that involve Chips.
        // Sample data. You should delegate this stuff to data access layer.
        return 
            new StoreDeal { 
                Code = "CS4.99", 
                Name = "Chips and salsa @ $4.99",
                Cost = 4.99,
                Items = new List<StoreItem> {  
                    new StoreItem { Code = "6732", Name = "Chips" },
                    new StoreItem { Code = "4900", Name = "Salsa" }
                }
            }
    }
}

public class CartService 
{
    private Cart cart;
    private DealService dealService;

    // ctor - inject dependencies
    public CartService(Cart cart, DealService dealService) {
        this.cart = cart;
        this.dealService = dealService;
    }

    public void AddItem(CartItem item) {
        var found = cart.Items.Find(i => i.Code == item.Code);
        if (found != null) { // Update quantity
            found.UpdateQuantity(found.Quantity + 1);
        }
        else { // Add new item
            item.Prepare();
            cart.Items.Add(item);
        }
    }

    public void RemoveItem(string code) {
        var found = cart.Items.Find(i => i.Code)
        if (found != null) {
            cart.Items.Remove(found);
        }
    }

    public void CalculateTotal() {
        // Used for checking which items in cart have got deal applied on.
        // We don't want "CS4.99" deal applied to both Chips and Salsa, for ex. Only for one of them.
        // So this collection simply holds deal codes already applied.
        var dealsApplied = new List<string>();

        foreach(var item in cart.Items) {
            // Check deal
            StoreDeal deal = dealService.GetDeal(item.Code);
            // Apply the logic for TotalAmount, TotalDiscount, FinalAmount
        }
    }
}

请注意,如果你要将这样的系统设计为真实的,那么将会有比上面更多的类。例如,在实际情况下,“芯片”不是项目,它是一种项目,因此不能拥有代码。但是,“Lays Potato Chips”将是一个单独的“Chips”类型的项目,它拥有自己的Code。此外,StoreItem将成为抽象实体,它是由EdibleItem,PersonalCareItem,HealthCareItem,CosmeticItem等类型派生的,以及存在于真实商店中的任何东西,其中EdibleItem将特别具有营养信息,该信息不适用于此列表中的其他信息。

最后,我刚写了这个(不完整的)代码,没有测试过!在你看到评论的地方,代码是不完整的,故意这样做是因为我不想让你盲目地欺骗采访。 :)

答案 1 :(得分:1)

我允许自己添加一些设计模式。我也把它视为锻炼对我来说:) 让我们分解它,然后通过我的思考过程:

  1. 系统中的数字和名称表示为类型
  2.   
        
    • ID - 四位数 - unsigned short int - 我们只需要0到9999之间的数字,这意味着我们只需要14位(2 ^ 14 = 16384)   和unsigned short int使用16位。这为我们提供了空间   想将项目数量增加到65536(2 ^ 16)
    •   
    • Name - string UTF-16,通常已经足够好了。但是我们必须记住,有些产品可能来自远方国家并使用   各种人物。 UTF-16每个字符只有16位(再次   65536),所以我们必须记住在我们的实施中,如果我们需要的话   我们需要使用不同编码的更多位(如UTF-32)
    •   
    • PriceDiscount - float足够好吗?我认为不是。在许多语言中,执行各种算术运算导致不完全   正确的答案(尝试使用JavaScript 0.2 * 0.4 - 您可以使用您的   浏览器的控制台)。谈到金钱,你不想增加   价格或宽松的钱。有特殊的库和类型   在处理金钱方面,实施是安全的
    •   
    1. 数据结构和类
    2.   
          
      • Item - 包含以下字段的类/结构:IDNameOriginal price(上述类型)
      •   
      • Item Collection - 数组 - 我们使用short unsigned int作为ID,我们也可以使用它来索引数组。我们甚至可以初始化   整个数组并在应用启动时将所有项目加载到内存中   起来。
      •   
      • PriceModifier - ID - short unsigned intType - ENUM代表折扣(或税)的类型,并知道在   case,it applied,Name - 字符串UTF-16(仅用于通知用户),   Value - short signed int百分比修饰符
      •   
      • PairPriceModifier - class - 扩展PriceModifier并添加一对项IDs
      •   
      • PriceModifier Collection - 我们需要使用快速搜索的结构,因为我们使用ID作为short usigned int,我们可以使用   数组数组。该列表不会经常变化。当我们需要添加/删除修饰符时,我们可以在线性时间内完成   O(1)* O(m),其中m是Item的修饰符长度。我们不会复制   物理修改器,因为我们可以使用对PriceModifier的引用   对象。
      •   
      • Shopping Cart Collection - 每个商店的主要功能是购物车,它必须优化为:添加/删除项目和列表   保存的项目。我们需要经常添加/删除元素并检查   已添加项目以应用各种折扣,但我们也想   列出所有已保存的项目(例如在购物车视图中)。我们可以用   带有数组哈希表 [在搜索/添加/删除项目时有O(1),在列出时有O(n)我们更新   添加/删除项目时的最终价格。在数组中我们保存了一个   引用类的对象,它表示购物中的项目   购物车知道应用了哪个PriceModifier
      •   
      • CartItem - class包含字段:ItemID - short unsigned intQuantity - unsigned intAppliedPriceModifier Collection -   哈希表可轻松访问已应用PriceModifiers数组,以便在购物车视图中列出这些内容。
      •   
      • AppliedPriceModifier - class包含字段:PriceModifierIDQuantity
      •   
           

      有趣的案例:如果我们在Shopping Cart Collection中添加一些元素,我们需要检查PriceModifier Collection。在O(1)   我们可以访问我们必须申请的PriceModifiers的正确列表。   然后,如果我们发现PairPriceModifier,我们需要再次检查Shopping Cart Collection,如果我们在那里没有配对项目。该   整个操作需要:

           
          
      • O(1) - 获取长度为PriceModifier数组
      •   
      • O(m) - 通过数组并应用修饰符
      •   
      • O(1) - 如果我们找到PairPriceModifier,我们会从Shopping Cart Collection
      • 访问现有项目   
      • O(k)+ O(m) - 更新AppliedPriceModifier
      • 中两个项目Shopping Cart Collection的集合   
      • O(1) - 更新最终价格(下文解释)
      •   
      1. 设计模式
      2.   
        1. 另一个问题
        2. 我们可以扩展主题并询问:

            
              
          • 如何在数据库中表示您的数据
          •   
          • 如果我们想增加项目数并将其表示为10.000个字母数字字符
          • ,该怎么办?   
          • 怎么做,如果我们想保持我们的购物应用程序99.9%稳定并运行
          •   
          • 我们如何处理许多请求
          •   
          • 如果客户在购物车中应用折扣会发生什么情况,但系统管理员会从系统中删除该折扣   更多。
          •   

          请添加一些评论,因为它对我来说也是一个有趣的话题。我可以改进/改变什么?我没有想到什么?

答案 2 :(得分:0)

可能的步骤:

  • 定义数据库模型
  • Java中的镜像数据库模型
  • 实施DAO层 - &gt; CRUD操作
  • 实施服务层 - &gt;业务逻辑
  • 公开服务层 - &gt;例如REST
  • 实现客户端,即客户端面向公开的服务层

答案 3 :(得分:0)

好的,这是一个没有任何设计模式的逻辑视角的答案。 物品代码和价格的表格很好。 因此,如果列表中的任何项目没有交易和奖励,那么只需使用此表,计算总价并完成结账。 现在,如果在结账清单中有任何产品的交易/奖励,您将不得不经历各种组合。

保持问题陈述简单:让我们假设列表中有3个项目。

所以你必须看看以下组合: Item1,Item2,Item3 Item1,Item2 第1项,第3项 Item2 Item3

现在,对于这些组合中的任何一种,您都必须搜索您的交易/奖励清单。这就像是说我正在使用Google搜索字符串&#34; Item1 Item2&#34;因此,我获得了一组网络链接,这些链接是此组合的交易/奖励。

在组合中,我现在检查哪一个是活跃的,并在其上应用交易价格。

设置交易价格后,从结账清单中删除项目组合,然后继续列表中的其余项目。

我希望这会有所帮助。