我是软件开发人员,我开始像软件开发人员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?
感谢所有帮助我集体讨论该系统设计的人。
答案 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)
我允许自己添加一些设计模式。我也把它视为锻炼对我来说:) 让我们分解它,然后通过我的思考过程:
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)Price
或Discount
-float
足够好吗?我认为不是。在许多语言中,执行各种算术运算导致不完全 正确的答案(尝试使用JavaScript0.2 * 0.4
- 您可以使用您的 浏览器的控制台)。谈到金钱,你不想增加 价格或宽松的钱。有特殊的库和类型 在处理金钱方面,实施是安全的
Item
- 包含以下字段的类/结构:ID
,Name
,Original price
(上述类型)Item Collection
- 数组 - 我们使用short unsigned int
作为ID,我们也可以使用它来索引数组。我们甚至可以初始化 整个数组并在应用启动时将所有项目加载到内存中 起来。PriceModifier
-ID
-short unsigned int
,Type
- 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 int
,Quantity
-unsigned int
,AppliedPriceModifier Collection
- 哈希表可轻松访问已应用PriceModifiers
的数组,以便在购物车视图中列出这些内容。AppliedPriceModifier
-class
包含字段:PriceModifierID
,Quantity
有趣的案例:如果我们在
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) - 更新最终价格(下文解释)
- 对于整个APP结构,我认为使用 Inversion of Control 的某些框架会很好。为了保持我们的代码模块化,我们更容易进行单元测试 可以使用 dependecy injection pattern .
- For presentation layer we can use either MVC or its mutation - MVVM. It will give us dual binding ability to quickly update UI. Information about final price, deals and applied discounts or taxes is essential.
- For updating final price, we can use Observer pattern 。我们观察
Shopping Cart Collection
以及何时 进出的东西,我们可以不断更新最终价格 时间- 为了保持我们的代码紧凑,我们可以使用 chaining(Cascade)
- 您可以使用 Decorator pattern 向
PriceModifiers
添加额外功能 类中的更改(对于一种情况阻止的情况) 申请另一个PriceModifier
)- 为了确保
添加或删除CartItems Collection
免受购物车之外的更改,我们可以使用 Mediator设计模式。只有在PriceModifiers
Item
时,该调解员服务才会负责应用Cart Collection
我们可以扩展主题并询问:
- 如何在数据库中表示您的数据
- 如果我们想增加项目数并将其表示为10.000个字母数字字符
,该怎么办?- 怎么做,如果我们想保持我们的购物应用程序99.9%稳定并运行
- 我们如何处理许多请求
- 如果客户在购物车中应用折扣会发生什么情况,但系统管理员会从系统中删除该折扣 更多。
请添加一些评论,因为它对我来说也是一个有趣的话题。我可以改进/改变什么?我没有想到什么?
答案 2 :(得分:0)
可能的步骤:
答案 3 :(得分:0)
好的,这是一个没有任何设计模式的逻辑视角的答案。 物品代码和价格的表格很好。 因此,如果列表中的任何项目没有交易和奖励,那么只需使用此表,计算总价并完成结账。 现在,如果在结账清单中有任何产品的交易/奖励,您将不得不经历各种组合。
保持问题陈述简单:让我们假设列表中有3个项目。
所以你必须看看以下组合: Item1,Item2,Item3 Item1,Item2 第1项,第3项 Item2 Item3
现在,对于这些组合中的任何一种,您都必须搜索您的交易/奖励清单。这就像是说我正在使用Google搜索字符串&#34; Item1 Item2&#34;因此,我获得了一组网络链接,这些链接是此组合的交易/奖励。
在组合中,我现在检查哪一个是活跃的,并在其上应用交易价格。
设置交易价格后,从结账清单中删除项目组合,然后继续列表中的其余项目。
我希望这会有所帮助。