跳转至

05-设计模式实战

设计模式实战落地流程图

学习时间: 约3-4小时 难度级别: ⭐⭐⭐⭐ 中高级 前置知识: 设计原则、创建型/结构型/行为型模式 学习目标: 在真实项目场景中综合运用设计模式,识别反模式,掌握面试策略


🎯 学习目标

  1. 掌握实际项目中最常用的8种模式及其组合方式
  2. 通过电商系统、Web框架、游戏开发三个完整案例理解模式应用
  3. 识别常见反模式和过度设计的陷阱
  4. 掌握设计模式面试回答框架和20道高频题

目录


1. 最常用的8种模式

按实际项目使用频率排序:

排名 模式 类型 典型场景
1 策略 行为型 多种算法/规则切换
2 观察者 行为型 事件系统、数据同步
3 工厂方法 创建型 对象创建解耦
4 单例 创建型 全局资源管理
5 装饰器 结构型 动态添加功能
6 代理 结构型 缓存、权限、延迟加载
7 模板方法 行为型 框架扩展点
8 命令 行为型 撤销/重做、任务队列

原则:不要为了用模式而用模式。先写最简单的实现,当代码出现"坏味道"时再考虑引入设计模式。


2. 实战案例一:电商系统重构

场景描述

一个电商系统核心模块存在以下问题: - 促销规则用大量 if/elif 堆叠,难以新增规则 - 下单后需要触发多个操作(扣库存、发通知、记日志),耦合严重 - 支付渠道硬编码,新增支付方式需要改核心代码

重构方案:策略 + 工厂 + 观察者

Python
from abc import ABC, abstractmethod
from typing import Dict, List, Callable
from dataclasses import dataclass
from enum import Enum

# ================================
# 1. 策略模式:促销规则
# ================================
class PromotionStrategy(ABC):
    @abstractmethod
    def calculate(self, original_price: float) -> float:
        pass

    @abstractmethod
    def description(self) -> str:
        pass

class NoPromotion(PromotionStrategy):
    def calculate(self, price): return price
    def description(self): return "无优惠"

class PercentDiscount(PromotionStrategy):
    def __init__(self, percent):
        self.percent = percent

    def calculate(self, price):
        return price * (1 - self.percent / 100)

    def description(self):
        return f"{self.percent}%折扣"

class FullReduction(PromotionStrategy):
    """满减"""
    def __init__(self, threshold, reduction):
        self.threshold = threshold
        self.reduction = reduction

    def calculate(self, price):
        if price >= self.threshold:
            return price - self.reduction
        return price

    def description(self):
        return f"满{self.threshold}{self.reduction}"

class StackedPromotion(PromotionStrategy):
    """组合优惠:可叠加多种策略"""
    def __init__(self, strategies: List[PromotionStrategy]):
        self.strategies = strategies

    def calculate(self, price):
        result = price
        for s in self.strategies:
            result = s.calculate(result)
        return result

    def description(self):
        return " + ".join(s.description() for s in self.strategies)

# ================================
# 2. 工厂模式:支付渠道
# ================================
class PaymentProcessor(ABC):
    @abstractmethod
    def process(self, amount: float, order_id: str) -> bool:
        pass

class AlipayProcessor(PaymentProcessor):
    def process(self, amount, order_id):
        print(f"  💳 Alipay: Processing ¥{amount:.2f} for {order_id}")
        return True

class WeChatPayProcessor(PaymentProcessor):
    def process(self, amount, order_id):
        print(f"  💳 WeChatPay: Processing ¥{amount:.2f} for {order_id}")
        return True

class CreditCardProcessor(PaymentProcessor):
    def process(self, amount, order_id):
        print(f"  💳 CreditCard: Processing ¥{amount:.2f} for {order_id}")
        return True

class PaymentFactory:
    """支付处理器工厂"""
    _processors = {
        "alipay": AlipayProcessor,
        "wechat": WeChatPayProcessor,
        "credit_card": CreditCardProcessor,
    }

    @classmethod
    def create(cls, method: str) -> PaymentProcessor:
        processor_cls = cls._processors.get(method)
        if not processor_cls:
            raise ValueError(f"Unsupported payment: {method}")
        return processor_cls()

    @classmethod
    def register(cls, name: str, processor_cls):
        """注册新支付方式,无需修改工厂代码"""
        cls._processors[name] = processor_cls

# ================================
# 3. 观察者模式:订单事件
# ================================
class EventBus:
    """全局事件总线"""
    _instance = None

    def __new__(cls):
        if not cls._instance:
            cls._instance = super().__new__(cls)  # super()调用父类方法
            cls._instance._listeners = {}
        return cls._instance

    def on(self, event: str, listener: Callable):
        self._listeners.setdefault(event, []).append(listener)

    def emit(self, event: str, data=None):
        for listener in self._listeners.get(event, []):
            listener(data)

# ================================
# 整合:OrderService
# ================================
@dataclass
class OrderItem:
    name: str
    price: float
    quantity: int

class OrderService:
    def __init__(self):
        self.event_bus = EventBus()

    def create_order(self, items: List[OrderItem], promotion: PromotionStrategy,
                     payment_method: str) -> dict:
        # 1. 计算价格
        subtotal = sum(item.price * item.quantity for item in items)
        total = promotion.calculate(subtotal)

        order = {
            "id": f"ORD-{id(items) % 10000:04d}",
            "items": items,
            "subtotal": subtotal,
            "promotion": promotion.description(),
            "total": total,
        }

        print(f"\n📦 Order {order['id']}:")
        print(f"   Subtotal: ¥{subtotal:.2f}")
        print(f"   Promotion: {promotion.description()}")
        print(f"   Total: ¥{total:.2f}")

        # 2. 支付(工厂模式创建处理器)
        processor = PaymentFactory.create(payment_method)
        if not processor.process(total, order["id"]):
            raise Exception("Payment failed")

        # 3. 发布事件(观察者模式解耦后续操作)
        self.event_bus.emit("order_created", order)
        self.event_bus.emit("payment_completed", {"order_id": order["id"], "amount": total})

        return order

# ====== 注册事件处理器(各服务独立) ======
event_bus = EventBus()

# 库存服务
event_bus.on("order_created", lambda order:
    print(f"  📦 Inventory: Deducting stock for {order['id']}"))

# 通知服务
event_bus.on("order_created", lambda order:
    print(f"  📧 Notification: Sending confirmation for {order['id']}"))

# 积分服务
event_bus.on("payment_completed", lambda data:
    print(f"  ⭐ Points: Adding {int(data['amount'])} points"))

# 日志服务
event_bus.on("order_created", lambda order:
    print(f"  📝 Logger: Order {order['id']} logged"))

# ====== 使用 ======
service = OrderService()

items = [
    OrderItem("Python Book", 89.0, 1),
    OrderItem("Keyboard", 299.0, 1),
]

# 满300减50 + 会员9折
promo = StackedPromotion([
    FullReduction(300, 50),
    PercentDiscount(10),
])

order = service.create_order(items, promo, "alipay")

重构效果

问题 模式 效果
促销规则硬编码 策略模式 新增促销只需加一个类
下单后操作耦合 观察者模式 各服务独立订阅,互不影响
支付渠道硬编码 工厂模式 注册新支付方式不改核心代码

3. 实战案例二:Web框架中的模式

Django中的设计模式

模式 Django中的体现
模板方法 View 类的 dispatch() 调用 get()/post() — 骨架固定,具体操作子类实现
策略 Authentication Backend — 可配置多种认证策略
观察者 Signalspre_save, post_save)— 模型操作后自动触发
代理 QuerySet 延迟查询 — 直到真正需要数据才执行SQL
装饰器 @login_required, @cache_page
责任链 Middleware 链式处理请求
外观 django.shortcuts 简化复杂操作

Flask中的模式

Python
from flask import Flask, request, jsonify
from functools import wraps

app = Flask(__name__)

# ====== 装饰器模式:认证 ======
def require_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get("Authorization")
        if not token or token != "Bearer valid_token":
            return jsonify({"error": "Unauthorized"}), 401
        return f(*args, **kwargs)
    return decorated

# ====== 策略模式:序列化 ======
class ResponseSerializer:
    """不同格式的响应序列化"""
    serializers = {
        "json": lambda data: jsonify(data),
        "xml": lambda data: f"<response>{data}</response>",
    }

    @classmethod
    def serialize(cls, data, fmt="json"):
        serializer = cls.serializers.get(fmt, cls.serializers["json"])
        return serializer(data)

# ====== 责任链模式:中间件 ======
@app.before_request
def log_request():
    print(f"[LOG] {request.method} {request.path}")

@app.before_request
def check_rate_limit():
    # 简化的限流检查
    pass

# ====== 使用 ======
@app.route("/api/users")
@require_auth
def get_users():
    users = [{"id": 1, "name": "Alice"}]
    fmt = request.args.get("format", "json")
    return ResponseSerializer.serialize(users, fmt)

Spring框架中的模式

模式 Spring中的体现
工厂 BeanFactory / ApplicationContext — IoC容器
单例 Bean默认scope为singleton
代理 Spring AOP — 基于JDK动态代理/CGLIB
模板方法 JdbcTemplate, RestTemplate
观察者 ApplicationEvent / @EventListener
策略 HandlerMapping 决定请求交给哪个Controller
责任链 Filter 链、HandlerInterceptor

4. 实战案例三:游戏开发模式

场景:2D RPG游戏核心架构

Python
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import List, Dict
from enum import Enum
import copy

# ================================
# 1. 状态模式:角色状态机
# ================================
class CharacterState(ABC):
    @abstractmethod
    def handle_input(self, character, action): pass

    @abstractmethod
    def update(self, character): pass

class IdleState(CharacterState):
    def handle_input(self, character, action):
        if action == "move":
            character.state = MovingState()
        elif action == "attack":
            character.state = AttackingState()

    def update(self, character):
        character.stamina = min(100, character.stamina + 1)  # 休息恢复

class MovingState(CharacterState):
    def handle_input(self, character, action):
        if action == "stop":
            character.state = IdleState()
        elif action == "attack":
            character.state = AttackingState()

    def update(self, character):
        character.stamina -= 0.5

class AttackingState(CharacterState):
    def __init__(self):
        self.frames = 0

    def handle_input(self, character, action):
        pass  # 攻击中不接受输入

    def update(self, character):
        self.frames += 1
        character.stamina -= 2
        if self.frames >= 10:  # 攻击动画结束
            character.state = IdleState()

# ================================
# 2. 策略模式:攻击方式
# ================================
class AttackStrategy(ABC):
    @abstractmethod
    def calculate_damage(self, attacker, target) -> int:
        pass

class MeleeAttack(AttackStrategy):
    def calculate_damage(self, attacker, target):
        damage = attacker.strength * 2 - target.defense
        return max(1, damage)

class MagicAttack(AttackStrategy):
    def calculate_damage(self, attacker, target):
        damage = attacker.intelligence * 3 - target.magic_defense
        return max(1, damage)

class RangedAttack(AttackStrategy):
    def calculate_damage(self, attacker, target):
        damage = attacker.dexterity * 2.5 - target.defense * 0.5
        return max(1, int(damage))

# ================================
# 3. 观察者模式:游戏事件
# ================================
class GameEventSystem:
    _handlers: Dict[str, List] = {}

    @classmethod  # @classmethod类方法,第一个参数为类本身
    def on(cls, event, handler):
        cls._handlers.setdefault(event, []).append(handler)

    @classmethod
    def emit(cls, event, **kwargs):  # *args接收任意位置参数;**kwargs接收任意关键字参数
        for handler in cls._handlers.get(event, []):
            handler(**kwargs)

# ================================
# 4. 命令模式:可撤销操作
# ================================
class GameCommand(ABC):
    @abstractmethod
    def execute(self): pass
    @abstractmethod
    def undo(self): pass

class MoveCommand(GameCommand):
    def __init__(self, character, dx, dy):
        self.character = character
        self.dx, self.dy = dx, dy

    def execute(self):
        self.character.x += self.dx
        self.character.y += self.dy

    def undo(self):
        self.character.x -= self.dx
        self.character.y -= self.dy

# ================================
# 5. 原型模式:怪物批量生成
# ================================
@dataclass  # 自动生成__init__等方法
class Monster:
    name: str
    hp: int
    attack: int
    defense: int
    loot: List[str] = field(default_factory=list)

    def clone(self):
        return copy.deepcopy(self)

class MonsterFactory:
    """原型工厂:预设怪物模板"""
    _prototypes: Dict[str, Monster] = {}

    @classmethod
    def register(cls, name, prototype: Monster):
        cls._prototypes[name] = prototype

    @classmethod
    def create(cls, name, **overrides) -> Monster:
        proto = cls._prototypes.get(name)
        if not proto:
            raise ValueError(f"Unknown monster: {name}")
        monster = proto.clone()
        for key, value in overrides.items():
            setattr(monster, key, value)  # hasattr/getattr/setattr动态操作对象属性
        return monster

# ====== 注册怪物模板 ======
MonsterFactory.register("slime", Monster("Slime", hp=30, attack=5, defense=2, loot=["jelly"]))
MonsterFactory.register("goblin", Monster("Goblin", hp=50, attack=12, defense=5, loot=["gold", "dagger"]))
MonsterFactory.register("dragon", Monster("Dragon", hp=500, attack=80, defense=40, loot=["dragon_scale"]))

# 批量生成:基于原型快速创建不同变种
slime1 = MonsterFactory.create("slime")
slime_boss = MonsterFactory.create("slime", name="King Slime", hp=200, attack=25)
goblin = MonsterFactory.create("goblin")

print(f"{slime1.name}: HP={slime1.hp}")          # Slime: HP=30
print(f"{slime_boss.name}: HP={slime_boss.hp}")   # King Slime: HP=200

# ====== 注册事件处理 ======
GameEventSystem.on("monster_killed", lambda **kw:  # lambda匿名函数:简洁的单行函数
    print(f"  🎉 {kw['monster']} defeated! Loot: {kw.get('loot', [])}"))

GameEventSystem.on("monster_killed", lambda **kw:
    print(f"  ⭐ +{kw.get('exp', 0)} EXP"))

# 触发事件
GameEventSystem.emit("monster_killed", monster="Goblin", loot=["gold"], exp=50)

游戏开发常用模式总结

模式 在游戏中的用途
状态 角色状态机(idle/move/attack/dead)
策略 AI行为、攻击方式、移动算法
观察者 成就系统、UI更新、音效触发
命令 回放系统、网络同步、撤销
原型 NPC/怪物批量生成
享元 地图Tile、粒子系统
组合 场景图、UI组件树

5. 反模式与过度设计

常见反模式

1. God Object(上帝对象)

Python
# ❌ 反模式:一个类做所有事
class Application:
    def handle_request(self): ...
    def connect_database(self): ...
    def send_email(self): ...
    def render_template(self): ...
    def validate_input(self): ...
    def generate_report(self): ...
    def process_payment(self): ...
    # 几千行代码...

2. Singleton滥用

Python
# ❌ 反模式:把所有共享状态都做成单例
class UserManager: ...      # 单例
class OrderManager: ...     # 单例
class ProductManager: ...   # 单例
class ConfigManager: ...    # 单例
# 本质上是全局变量,不利于测试和并发

# ✅ 应该用依赖注入
class OrderService:
    def __init__(self, user_repo, product_repo, config):
        self.user_repo = user_repo
        self.product_repo = product_repo
        self.config = config

3. 过度设计

Python
# ❌ 过度设计:一个简单功能用了3个模式
class AbstractLoggerFactory(ABC): ...  # ABC抽象基类;abstractmethod强制子类实现
class ConsoleLoggerFactory(AbstractLoggerFactory): ...
class FileLoggerFactory(AbstractLoggerFactory): ...
class LoggerDecorator: ...
class TimestampDecorator(LoggerDecorator): ...
class LevelDecorator(LoggerDecorator): ...
# 实际只需要打印日志...

# ✅ 简单实现
import logging
logger = logging.getLogger(__name__)
logger.info("This is enough!")

何时引入设计模式

三次法则:当同样的问题出现三次时,才考虑引入设计模式。

Text Only
1. 第一次:直接写最简单的实现
2. 第二次:可以忍受一些重复
3. 第三次:考虑重构,引入适当的设计模式

代码"坏味道"→ 对应模式

坏味道 建议模式
大量 if/elif/else 分支 策略模式 / 状态模式
一个改动需要修改多处 观察者模式
对象创建逻辑散落各处 工厂模式
需要扩展功能但不想继承 装饰器模式
复杂子系统API难用 外观模式
大量重复的大对象 享元模式

6. 面试策略与高频题

面试回答框架 STAR-P

  1. Situation — 什么场景/问题
  2. Task — 需要解决什么
  3. Action — 选择了什么模式,为什么
  4. Result — 带来了什么好处
  5. Principle — 遵循了什么设计原则

例如:"在电商系统中(S),促销规则频繁变化(T),我们使用策略模式将每种促销封装为独立类(A),新增促销只需添加类不用改核心代码(R),符合开闭原则(P)。"

20道高频面试题

基础概念

Q1: 什么是设计模式? A: 设计模式是针对反复出现的软件设计问题的通用可复用解决方案。由GoF提出23种经典模式,分为创建型、结构型、行为型三类。它不是可以直接使用的代码,而是经过验证的设计思想。

Q2: 面向对象设计的SOLID原则是什么? A: S单一职责、O开闭原则、L里氏替换、I接口隔离、D依赖倒置。其中开闭原则最重要——对扩展开放、对修改关闭,几乎所有设计模式都在实践这个原则。

Q3: 组合优于继承,为什么? A: 继承是编译时绑定的强耦合关系,组合是运行时灵活调整的弱耦合。继承破坏封装性(子类依赖父类实现),组合只依赖接口。策略模式、装饰器模式都是组合优于继承的典型体现。

创建型

Q4: 单例模式如何保证线程安全? A: Python可用模块级变量(天然线程安全)或 threading.Lock + 双重检查;Java推荐用枚举实现(最简单安全)或 volatile + synchronized 双重检查锁。

Q5: 工厂方法和抽象工厂的区别? A: 工厂方法创建一种产品,靠子类决定创建哪种。抽象工厂创建一族相关产品(如按钮+输入框+弹框),保证产品间风格一致。

Q6: 建造者模式和工厂模式的区别? A: 工厂关注"创建什么"(不同类型产品),建造者关注"如何创建"(相同类型但不同配置的复杂对象)。建造者适合参数很多的对象构造。

结构型

Q7: 装饰器和代理模式的区别? A: 装饰器为对象添加新功能(如Java IO的BufferedReader包装FileReader),客户端通常知道有装饰。代理控制对对象的访问(如缓存代理、权限代理),对客户端透明。

Q8: 适配器模式的实际用例? A: 统一多个第三方支付SDK接口、旧系统到新系统的接口适配、不同数据格式之间的转换层。核心是让不兼容的接口协同工作。

Q9: 外观模式解决什么问题? A: 为复杂子系统提供简单统一接口。如 requests.get(url) 封装了Socket连接、HTTP协议构造、SSL握手等复杂操作。

Q10: 享元模式和缓存的区别? A: 享元模式共享不可变的内部状态以节省内存(如字体渲染中的字形对象)。缓存存储可变的计算结果以节省时间。享元关注内存,缓存关注速度。

行为型

Q11: 策略模式和状态模式的区别? A: 策略模式由客户端外部选择算法,策略互不知道对方。状态模式的状态对象知道其他状态,转换由内部自动完成。策略是"做什么"的选择,状态是"处于什么阶段"的管理。

Q12: 观察者模式和发布-订阅的区别? A: 经典观察者模式中Subject直接通知Observer(紧耦合)。发布-订阅引入了消息中间件/事件总线,发布者和订阅者完全解耦。Redis Pub/Sub、Kafka就是发布-订阅。

Q13: 命令模式如何实现撤销? A: 每个命令对象实现 execute()undo() 方法。执行时压入历史栈,撤销时弹出栈顶调用 undo(),重做时从重做栈弹出调用 execute()

Q14: 模板方法和策略模式怎么选? A: 如果算法骨架固定只有部分步骤变化→模板方法(继承)。如果整个算法可替换→策略模式(组合)。一般优先策略模式,因为组合比继承灵活。

Q15: 责任链模式在Web开发中的应用? A: Django/Flask的中间件就是责任链。请求经过:日志→CORS→限流→认证→路由。每个中间件可以处理请求、修改请求/响应、或终止传递。

综合

Q16: 你在项目中用过哪些设计模式? A: (用STAR-P框架回答)以策略模式为例:需求是电商系统支持多种促销规则→每增加促销需改核心代码→将每种促销封装为策略类,通过工厂创建→新增促销只加一个类+配置,符合开闭原则。

Q17: 如何避免设计模式的过度使用? A: "三次法则"——问题出现三次再引入模式。优先选择最简单的方案,当代码出现"坏味道"(如重复分支、频繁修改)时才考虑模式。设计模式不是目的,而是解决问题的工具。

Q18: MVC是设计模式吗? A: MVC是架构模式,不是GoF设计模式,但内部用到了多种设计模式:观察者(Model通知View更新)、策略(Controller选择不同的处理方式)、组合(View组件树)。

Q19: 设计模式和设计原则的关系? A: 设计原则是"道",设计模式是"术"。SOLID是高层指导思想,设计模式是这些原则在特定场景下的具体实现方案。比如策略模式体现了开闭原则和依赖倒置。

Q20: 如何学习和掌握设计模式? A: (1)先理解SOLID原则 (2)学习高频5-8种模式 (3)阅读开源框架源码看模式如何应用 (4)在项目中重构时实践 (5)不要背概念,关注每个模式解决的核心问题。


7. 练习与自我检查

✏️ 练习题

  1. 电商系统:基于本章的电商案例,添加以下功能:
  2. 新增一种促销策略"限时折扣"(指定时间范围内打折)
  3. 新增"ApplePay"支付方式(通过工厂注册)
  4. 添加一个事件监听器:下单后给用户推送APP内通知

  5. 框架分析:阅读你常用的Web框架(Django/Flask/Spring)源码,找出至少5种设计模式的使用,写出文件位置和模式名称。

  6. 重构练习:将以下代码用合适的设计模式重构:

    Python
    def send_notification(user, message, channel):
        if channel == "email":
            # 50行邮件发送代码...
            pass
        elif channel == "sms":
            # 40行短信发送代码...
            pass
        elif channel == "wechat":
            # 60行微信发送代码...
            pass
        elif channel == "push":
            # 45行推送代码...
            pass
    

  7. 面试模拟:选择3个你最熟悉的设计模式,用STAR-P框架各准备一段面试回答。

  8. 迷你项目:用至少3种设计模式实现一个简易的任务管理器(支持添加/删除任务、撤销操作、任务状态流转、不同优先级处理策略)。

自我检查清单

  • 能在实际场景中识别"用哪个模式"
  • 理解策略+工厂+观察者的组合威力
  • 能指出常用Web框架中的设计模式
  • 知道什么时候不该用设计模式
  • 能用STAR-P框架回答"项目中的设计模式"面试题
  • 完成至少3道练习题

🎓 课程总结

恭喜你完成了设计模式的全部学习!回顾整个学习路线:

Text Only
设计原则(SOLID) → 创建型模式(5种) → 结构型模式(7种) → 行为型模式(11种) → 实战应用

核心收获: 1. 设计模式的本质是面向接口编程 + 组合优于继承 + 开闭原则 2. 不需要记住所有23种模式,掌握高频8种即可覆盖95%的场景 3. 最好的学习方式是在重构中自然引入,而不是强行套用

推荐下一步: 阅读《Head First设计模式》深入理解 → 阅读你使用的框架源码 → 在实际项目中实践