阶段二:进阶篇 ⭐⭐ 进阶

第 5 章:模块化开发与代码组织

用 SOLID 原则指导 AI — 构建可维护、可扩展的代码结构

🎯 本章学习目标

  1. 掌握分层架构中接口层、业务层、数据层的 AI 生成策略
  2. 能够用提示词让 AI 生成符合 SOLID 原则的代码
  3. 学会在 AI 编程中应用常用设计模式(策略、工厂、观察者)
  4. 建立"接口先行"的开发流程:先定义契约,再生成实现
  5. 掌握用提示词强制执行编码规范的技术

5.1 分层架构的 AI 生成策略

📡 接口层(API / Controller)

职责:接收请求、参数验证、调用业务层、返回响应

不应该做:包含业务逻辑、直接操作数据库

AI 生成策略:先给出完整的接口规格(路径、方法、参数、响应格式),让 AI 生成"薄"的控制层

⚙️ 业务层(Service / Domain)

职责:业务规则、工作流编排、事务管理

不应该做:关心 HTTP 细节、直接拼接 SQL

AI 生成策略:用业务规则描述(而非技术描述)来驱动 AI 生成

🗄️ 数据层(Repository / DAO)

职责:数据持久化、查询抽象、缓存管理

不应该做:包含业务判断、依赖上层模块

AI 生成策略:先定义 Repository 接口,再生成具体实现

💡 核心原则:依赖方向接口层 → 业务层 → 数据层。每一层只依赖它的下一层,绝不反向。AI 如果不遵守这个规则,代码会迅速腐化。在提示词中用约束明确声明:"业务层不能导入 FastAPI 的任何模块"。

5.2 SOLID 原则在 AI 生成代码中的应用

S — 单一职责(Single Responsibility)

提示词技巧:"每个类/函数只做一件事。如果它的描述需要用'和'连接两个不同的职责,就拆成两个。"

O — 开闭原则(Open/Closed)

提示词技巧:"使用策略模式或插件架构。新增功能时通过扩展新类而非修改现有代码。"

L — 里氏替换(Liskov Substitution)

提示词技巧:"子类必须能完全替代父类。在提示词中要求为接口生成验证测试,确保所有实现满足契约。"

I — 接口隔离(Interface Segregation)

提示词技巧:"不要创建一个大一统的接口。为每种使用场景定义精简的接口。AI 生成的接口方法不应超过 5 个。"

D — 依赖反转(Dependency Inversion)

提示词技巧:"高层模块和低层模块都依赖抽象。在提示词中要求使用 Protocol/ABC 定义抽象,具体实现通过依赖注入传入。"

5.3 设计模式提示词

策略模式(Strategy Pattern)

适用场景:NAS 系统中不同的文件存储后端(本地磁盘、网络存储、对象存储)

# AI 生成的策略模式框架
from abc import ABC, abstractmethod

class StorageBackend(ABC):
    """存储后端抽象接口"""
    @abstractmethod
    async def read(self, path: str, offset: int = 0, size: int = -1) -> bytes: ...
    @abstractmethod
    async def write(self, path: str, data: bytes) -> int: ...
    @abstractmethod
    async def delete(self, path: str) -> bool: ...

class LocalDiskBackend(StorageBackend):
    """本地磁盘实现"""
    async def read(self, path, offset=0, size=-1):
        # 使用 aiofiles 实现异步文件读取
        ...

class S3Backend(StorageBackend):
    """S3 兼容对象存储实现"""
    async def read(self, path, offset=0, size=-1):
        # 使用 aiobotocore 实现
        ...

class StorageManager:
    """存储管理器 — 依赖抽象,不依赖具体实现"""
    def __init__(self, backend: StorageBackend):
        self.backend = backend  # 依赖注入

观察者模式(Observer Pattern)

适用场景:NAS 系统中的事件通知(磁盘故障、用户登录、配置变更)

class EventBus:
    """轻量级事件总线 — 发布/订阅模式"""
    def __init__(self):
        self._handlers: dict[str, list[callable]] = {}

    def subscribe(self, event: str, handler: callable):
        self._handlers.setdefault(event, []).append(handler)

    async def publish(self, event: str, **data):
        for handler in self._handlers.get(event, []):
            await handler(**data)

# 使用示例
bus = EventBus()
bus.subscribe("disk.failure", send_alert_email)
bus.subscribe("disk.failure", log_to_syslog)
await bus.publish("disk.failure", device="/dev/sdb", reason="SMART error")

📋 本章提示词模板

模板 5.1:接口先行开发模板

📋
请按接口先行的方式实现以下功能模块:

## 第一步:生成接口定义
请先定义抽象接口(使用 Python Protocol 或 ABC):
- 接口名:[名称]
- 方法列表:[方法名、参数、返回值、异常]
- 每个方法有完整的类型注解和 docstring

## 第二步:生成接口验证测试
为接口生成测试用例(不需要实现,使用 Mock):
- 验证接口契约的完整性
- 验证各种实现的互换性(里氏替换测试)

## 第三步:生成默认实现
基于接口定义,生成一个完整的默认实现:
- 遵循 [Repository / Service / Adapter] 模式
- 所有 IO 操作异步
- 完整的错误处理

模板 5.2:设计模式应用模板

📋
我们需要实现 [功能描述]。请使用 [设计模式名称] 进行设计。

## 约束
- 语言:[Python 3.10+ / TypeScript / ...]
- 遵循 [设计模式] 的经典结构
- 所有组件接口化,支持替换实现
- 提供至少 2 个具体实现的示例
- 包含使用示例和集成测试

## 输出
1. 类图描述(Mermaid 格式)
2. 接口/抽象类定义
3. 至少 2 个具体实现
4. 工厂/注册机制(如何选择具体实现)
5. 使用示例

模板 5.3:代码规范强制执行模板

📋
以下是你生成所有代码时必须遵守的规范。每一条都是强制性的。

## 命名规范
- 模块/文件:[kebab-case / snake_case]
- 类:[PascalCase]
- 函数/方法:[snake_case]
- 常量:[UPPER_SNAKE_CASE]
- 私有成员:以单下划线 _ 开头

## 代码结构
- 每个函数不超过 30 行(含 docstring)
- 每个类不超过 200 行
- 每个模块不超过 500 行
- 函数参数不超过 5 个(超过则用 dataclass 封装)

## 文档规范
- 每个公开函数使用 Google-style docstring
- 复杂逻辑处用行注释说明"为什么"(不是"做什么")
- 每个模块有模块级 docstring

## 错误处理
- 不使用裸 except:
- 异常信息必须包含足够的调试上下文
- 自定义异常类继承自项目基类 [BaseError]

## 代码质量
- 零类型警告(mypy --strict)
- 零 lint 警告(pylint / ruff)
- 测试覆盖所有公开接口

请在每次生成代码后,自检是否满足以上所有规范。

🛠️ 实战演练:NAS 存储管理模块的分层实现

从接口到实现的全流程

Step 1:定义 Repository 接口

from abc import ABC, abstractmethod
from typing import Optional
from dataclasses import dataclass

@dataclass
class DiskInfo:
    device: str           # /dev/sda
    mount_point: str      # /mnt/data
    fs_type: str          # ext4
    total_gb: float
    used_gb: float
    uuid: str
    model: str

class IDiskRepository(ABC):
    """磁盘信息数据访问接口"""
    @abstractmethod
    async def list_all(self) -> list[DiskInfo]: ...
    @abstractmethod
    async def get_by_device(self, device: str) -> Optional[DiskInfo]: ...
    @abstractmethod
    async def get_mounted(self) -> list[DiskInfo]: ...

class IMountRepository(ABC):
    """挂载操作数据访问接口"""
    @abstractmethod
    async def mount(self, device: str, mount_point: str, fs_type: str) -> bool: ...
    @abstractmethod
    async def unmount(self, mount_point: str, force: bool = False) -> bool: ...
    @abstractmethod
    async def get_fstab_entries(self) -> list[dict]: ...

Step 2:AI 生成业务层

class DiskService:
    """磁盘管理业务层 — 不依赖 FastAPI,不依赖具体数据源"""
    def __init__(self, disk_repo: IDiskRepository, mount_repo: IMountRepository):
        self._disks = disk_repo
        self._mounts = mount_repo

    async def get_dashboard_data(self) -> dict:
        """聚合仪表盘所需的磁盘概览数据"""
        all_disks = await self._disks.list_all()
        mounted = [d for d in all_disks if d.mount_point]
        total = sum(d.total_gb for d in mounted)
        used = sum(d.used_gb for d in mounted)
        return {
            "disk_count": len(all_disks),
            "mounted_count": len(mounted),
            "total_gb": round(total, 2),
            "used_gb": round(used, 2),
            "usage_percent": round((used / total * 100) if total else 0, 1),
            "disks": [self._to_dict(d) for d in all_disks],
        }

Step 3:AI 生成接口层(FastAPI 路由)

from fastapi import APIRouter, Depends, HTTPException

router = APIRouter(prefix="/api/disks", tags=["磁盘管理"])

@router.get("/")
async def list_disks(service: DiskService = Depends(get_disk_service)):
    """获取所有磁盘信息"""
    return await service.get_dashboard_data()

@router.post("/mount")
async def mount_disk(
    device: str, mount_point: str, fs_type: str = "auto",
    service: DiskService = Depends(get_disk_service)
):
    """挂载磁盘"""
    try:
        success = await service.mount_disk(device, mount_point, fs_type)
        if not success:
            raise HTTPException(400, "挂载失败")
        return {"status": "mounted", "device": device}
    except ValueError as e:
        raise HTTPException(400, str(e))

审查要点

  • ✅ 依赖方向正确:API → Service → Repository,没有反向依赖
  • ✅ Service 层没有导入 FastAPI 的任何类
  • ✅ 使用依赖注入,方便单元测试
  • ✅ 接口与实现分离,可以随时替换 Repository 实现
  • ⚠️ 依赖注入容器(get_disk_service)需要额外管理

⚠️ 常见坑点

🕳️ 坑 1:AI 倾向于把逻辑塞进一个函数

AI 默认会生成"上帝函数"——一个函数做完所有事。在提示词中加约束:"每个函数只做一件事,如果描述中包含'和'字,拆成两个函数。"

🕳️ 坑 2:分层变跳层

AI 可能让 API 层直接调用 Repository,跳过 Service 层。在提示词中显式声明:"API 层只能调用 Service 层,不能直接调用 Repository。"

🕳️ 坑 3:设计模式过度使用

AI 可能为了"专业"而引入不必要的设计模式。简单场景下,一个函数就够。始终问自己:这个模式解决的是真实存在的复杂性,还是臆想的?

📝 本章小结

  • 分层架构的核心是依赖方向:API → Service → Repository,绝不反向
  • SOLID 原则不是教条,而是防止代码腐化的"免疫系统"
  • 接口先行:先定义契约再实现,AI 是这个流程的加速器
  • 常用设计模式(策略、观察者、工厂)用提示词可以精确控制 AI 生成的结构
  • 规范提示词(模板 5.3)应该放在 CLAUDE.md 中,让 AI 每次生成都遵守

🤔 思考练习

  1. 选择一个你现有的模块,用接口先行的方式为它定义 Protocol 接口,然后让 AI 基于接口重新生成实现。对比旧代码的耦合度。
  2. 用策略模式重构一个 switch-case 或 if-elif 链(如支付方式选择、文件格式解析)。
  3. 撰写一份你的团队的编码规范提示词(参考模板 5.3),放入项目的 CLAUDE.md 中。