设计一个餐厅点餐与菜单定价系统。系统需要支持多种类型的菜单商品,其中 Pizza 是核心商品之一。
每个 Pizza 由一个 Crust、一个 Size 和 0 到 N 个 Topping 组成。Crust 具有名称和基础价格,Size 具有名称和价格倍率,Topping 具有名称和价格。
披萨价格按照以下公式计算:Price = (base_price + sum(toppings_prices)) * size_multiplier。
系统需要支持动态增加新的 Size,例如 Small、Medium、Large,以及未来新增的其他尺寸,而不应该修改核心价格计算逻辑。
系统还需要支持多种菜单商品类型,例如 Pizza、Pasta 等,并允许一个 Order 同时包含不同类型的 MenuItem。
当新增一种菜单商品类型时,不应该修改已有的订单计算逻辑。每种菜单商品应该能够独立计算自己的价格,并由 Order 统一计算所有商品的总价。
金额计算需要保证精度,同时需要对非法价格、非法尺寸倍率等输入进行校验。
示例 1:
输入:
Crust = Thin Crust, price = $8
Size = Small, multiplier = 0.75
Toppings = [Cheese, price = $2]
输出:
$7.50
解释:
8。Cheese 的价格为 2,因此基础价格与配料价格之和为 8 + 2 = 10。Small 的价格倍率为 0.75,因此最终价格为 10 * 0.75 = 7.50。Crust = Thin Crust, price = $8
Size = Medium, multiplier = 1.0
Toppings = [Cheese, price = $2, Mushrooms, price = $1.5]
输出:
$11.50
解释:
8。Cheese 和 Mushrooms 的价格之和为 2 + 1.5 = 3.5。8 + 3.5 = 11.5。Medium 的倍率为 1.0,因此最终价格为 11.50。MenuItem 1 = Small Thin Crust Cheese Pizza
MenuItem 2 = Medium Thin Crust Mushroom Pizza
Order = [MenuItem 1, MenuItem 2]
输出:
Total Order Price = Price(MenuItem 1) + Price(MenuItem 2)
解释:
Order 可以同时包含多个不同类型的 MenuItem。MenuItem 独立计算自己的价格。Order 通过统一接口获取每个商品的价格,并计算最终订单总价。Size、Crust、Topping、Pizza 和 Order 分别建模为独立的领域对象。MenuItem 作为不同菜单商品的统一父类,并提供统一的 calculatePrice() 接口。Pizza 通过组合 Crust、Size 和多个 Topping 实现价格计算,避免针对不同属性组合创建大量子类。Size 不应该简单表示为 String 或 float,因为它包含 name、multiplier 等独立属性。Crust 和 Topping 同样具有独立的业务含义,因此应该分别建模为独立对象。Pizza 与 Crust、Size、Topping 之间属于组合关系,而不是继承关系。Pizza、Pasta 等不同商品都属于 MenuItem,因此 Order 可以通过多态统一处理不同商品。float 或 double,实际工程中可以使用 Decimal、BigDecimal 或整数 cents。Size 类,保存尺寸名称和价格倍率。Crust 类,保存底胚名称和基础价格。Topping 类,保存配料名称和价格。MenuItem 类,并定义 calculate_price()。Pizza 类继承 MenuItem,并组合 Crust、Size 和 Topping 列表。Pizza.calculate_price() 中计算底胚价格与所有配料价格之和,再乘以尺寸倍率。Pasta,继承 MenuItem 并实现自己的价格计算逻辑。Order 类保存多个 MenuItem,通过多态调用每个商品的 calculate_price() 并计算总价。Pizza 可以没有任何 Topping,此时配料总价为 0。Size 的 multiplier 应该为正数。Crust 和 Topping 的价格不能为负数。Order 可以同时包含 Pizza、Pasta 以及未来新增的 MenuItem 类型。Size 时,不应该修改 Pizza 的价格计算逻辑。MenuItem 类型时,不应该修改 Order 的总价计算逻辑。BOGO、满减券、特定配料折扣等动态促销规则,可以引入 PricingStrategy。Store 或 StoreContext 关联,实现多门店配置隔离。InventoryService,并在订单确认阶段执行库存校验与扣减。Size、Crust、Topping 或 Pizza 对象:O(1),不考虑 Topping 列表复制。K 个 Topping 的 Pizza 价格:O(K)。N 个商品、所有商品共有 T 个 Topping 的订单总价:O(N + T)。Pizza 需要 O(K) 空间保存其 Topping 引用。N 个商品的 Order 需要 O(N) 空间保存商品引用。from abc import ABC, abstractmethod
from decimal import Decimal, ROUND_HALF_UP
from typing import List
class Size:
def __init__(self, name: str, multiplier: Decimal):
if multiplier <= Decimal("0"):
raise ValueError("Multiplier must be positive")
self.name = name
self.multiplier = multiplier
class Crust:
def __init__(self, name: str, price: Decimal):
if price < Decimal("0"):
raise ValueError("Price cannot be negative")
self.name = name
self.price = price
class Topping:
def __init__(self, name: str, price: Decimal):
if price < Decimal("0"):
raise ValueError("Price cannot be negative")
self.name = name
self.price = price
class MenuItem(ABC):
def __init__(self, name: str):
self.name = name
@abstractmethod
def calculate_price(self) -> Decimal:
pass
class Pizza(MenuItem):
def __init__(self, name: str, crust: Crust, size: Size, toppings: List[Topping] = None):
super().__init__(name)
self.crust = crust
self.size = size
self.toppings = toppings if toppings is not None else []
def add_topping(self, topping: Topping) -> None:
self.toppings.append(topping)
def calculate_price(self) -> Decimal:
topping_total = sum((topping.price for topping in self.toppings), Decimal("0"))
subtotal = self.crust.price + topping_total
final_price = subtotal * self.size.multiplier
return final_price.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
class Pasta(MenuItem):
def __init__(self, name: str, base_price: Decimal):
super().__init__(name)
if base_price < Decimal("0"):
raise ValueError("Price cannot be negative")
self.base_price = base_price
def calculate_price(self) -> Decimal:
return self.base_price.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
class Order:
def __init__(self):
self.items: List[MenuItem] = []
def add_item(self, item: MenuItem) -> None:
self.items.append(item)
def calculate_total(self) -> Decimal:
total = sum((item.calculate_price() for item in self.items), Decimal("0"))
return total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
```掌握同类考点的变体套路与最优解模板,举一反三快速拿下技术面试: