超市系统的设计模式

The*_*ess 5 design-patterns point-of-sale

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

规则和要求:Super Foods 的每件商品都由唯一的四位数代码标识。今天,Super Foods 的定价方案使用以下定价类别,但要注意:价格在不断变化,销售部门总是在创造新的激励措施和优惠,例如买一送一。

EG:薯条和莎莎酱(#6732 和#4900)一起售价 4.99 美元,但单独售价分别为 2.49 美元和 3.49 美元。

EG2:买两支牙刷,每支 1.99 美元,送一支。

EG3:一瓶葡萄酒(货号 #0923)售价 15.49 美元,需额外缴纳 9.25% 的税

通读了设计模式后,这看起来很自然地适合某种形式的Decorator模式来汇总对象的销售额。带有模式的 SQLite 数据库<ID, ObjectName, Price>也会以某种方式有用,尽管我对如何在所有这些中创建数据访问对象感到生疏。

我试图用全栈 MVC 的心态来解决这个问题,我觉得我可能对某些事情生疏了。这就是 Spring 框架的著名之处吗?也许可以为这个用例推荐一个更好的 API?

感谢任何人帮助我集思广益地设计这个系统。

Nik*_*tak 2

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

软件设计非常困难,不可能一次性完成。另外,我确信您的潜在雇主对您的设计方式更感兴趣,而不是您使用的技术堆栈。所以我不会对此发表评论。这是你的决定。

根据您的要求,这些是我的初步想法。还有改进的空间(是的!)但至少这应该适用于您的任务范围。这是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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

最后,我只是写了这个(不完整的)代码,没有测试它!你看到评论的地方代码不完整,这是故意的,因为我不想让你盲目地在面试中作弊。:)