下一讲 — 第八模块:OOD实践
SOLID原则应用于完整领域、重构反模式、测试策略,以及完整的集成模型。
工厂方法、单例、观察者、策略——GoF经典模式应用于书店领域,附SysML v2映射。
设计模式是针对反复出现设计问题的可复用命名解决方案,由"四人帮"(1994年)整理为三类:
| 类别 | 意图 | 本模块示例 |
|---|---|---|
| 创建型 | 如何创建对象 | 工厂方法、单例 |
| 结构型 | 如何组合对象 | 装饰器 |
| 行为型 | 对象如何交互 | 观察者、策略 |
问题:创建Book对象的代码与具体类型耦合。添加AudioBook需要找到所有DigitalBook(...)调用点。
解决方案:工厂集中管理创建。调用者依赖Book接口,永不依赖具体类:
class BookFactory:
@staticmethod
def create(data: dict) -> Book:
t = data.get("type","").lower()
if t == "digital":
return DigitalBook(
isbn=data["isbn"], title=data["title"],
author=data["author"], price=data["price"],
file_format=data["file_format"],
file_size=data["file_size"],
download_url=data["download_url"],
)
elif t == "physical":
return PhysicalBook(
isbn=data["isbn"], title=data["title"],
author=data["author"], price=data["price"],
weight=data["weight"],
dimensions=data["dimensions"],
warehouse=data["warehouse"],
)
else:
raise ValueError(f"未知图书类型: {t!r}")
# 调用者使用Book接口——不了解具体类型
books: list[Book] = [BookFactory.create(d) for d in raw_data]
for b in books: print(b.get_details()) # 多态自动处理get_instance()都返回同一实例。问题:多个Inventory实例导致库存数据不一致。解决方案:确保最多存在一个实例(线程安全):
import threading
class Inventory:
_instance = None
_lock = threading.Lock()
def __new__(cls):
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._catalogue = {}
cls._instance._observers = []
return cls._instance
def add_book(self, book: Book):
self._catalogue[book.isbn] = book
def update_stock(self, isbn: str, delta: int):
book = self.get_book(isbn)
book.stock = max(0, book.stock + delta)
self._notify(isbn, book.stock)
# 两次调用返回相同对象
assert Inventory() is Inventory() # True问题:多个服务(邮件、仪表盘、补货)需要在库存变化时响应。从Inventory直接调用每个服务造成紧耦合。
解决方案:观察者模式。Inventory通知所有注册的观察者,无需知道它们做什么:
from abc import ABC, abstractmethod
class StockObserver(ABC):
@abstractmethod
def on_stock_change(self, isbn: str, new_qty: int) -> None: ...
class EmailAlertService(StockObserver):
def on_stock_change(self, isbn, new_qty):
if new_qty == 0:
print(f"邮件:{isbn}已缺货")
elif new_qty < 5:
print(f"邮件:库存预警——{isbn}:剩余{new_qty}本")
class DashboardService(StockObserver):
def on_stock_change(self, isbn, new_qty):
print(f"仪表盘:{isbn}库存更新为{new_qty}本")
# 观察者连接
inv = Inventory.get_instance()
inv.subscribe(EmailAlertService())
inv.subscribe(DashboardService())
inv.update_stock("978-1-11", -3)
# 邮件:库存预警——978-1-11:剩余2本
# 仪表盘:978-1-11库存更新为2本问题:定价逻辑因情境而异(标准、季节性、会员)。在Order中硬编码if/elif链脆弱且违反OCP。
解决方案:策略模式。每种算法是独立的类,可在运行时替换:
class PricingStrategy(ABC):
@abstractmethod
def calculate(self, base_price: float) -> float: ...
class StandardPricing(PricingStrategy):
def calculate(self, base): return base
class SeasonalPricing(PricingStrategy):
def __init__(self, factor): self._factor = factor
def calculate(self, base): return round(base * self._factor, 2)
class Order:
def __init__(self, oid, customer, strategy=None):
self._strategy = strategy or StandardPricing()
def total(self):
raw = sum(i.subtotal() for i in self._items)
return round(self._strategy.calculate(raw), 2)
def set_pricing(self, s: PricingStrategy): self._strategy = s
# 运行时策略替换
order = Order("O-001", customer)
print(order.total()) # £29.99——标准定价
order.set_pricing(SeasonalPricing(0.8))
print(order.total()) # £23.99——季节性八折| 模式 | SysML v2表达 |
|---|---|
| 工厂方法 | factory part def中的action def create;返回类型化的Book |
| 单例 | multiplicity [1]的part def Inventory;<<singleton>>构造型 |
| 观察者 | interface def StockObserver;Inventory向所有观察者发送流 |
| 策略 | interface def PricingStrategy;Order有ref strategy : PricingStrategy |
集中管理创建。调用者依赖抽象类型。添加新类型只需修改工厂。
唯一实例。全局一致状态。但:测试隔离更困难。
事件驱动。Subject不了解观察者。运行时添加/移除。
用可互换算法对象替换if/elif链。算法的OCP应用。
模式是命名解决方案,提供团队共同的设计语言。
理解问题再用模式。过度使用增加不必要的复杂性。
SOLID原则应用于完整领域、重构反模式、测试策略,以及完整的集成模型。