关于 design-patterns
design-patterns 是一项设计模式参考技能,涵盖 Rust、TypeScript/React 和 Django/Python。它根据具体情境比较方案与取舍,并提供反模式指导。
主要功能
- 01涵盖 Rust、TypeScript/React 和 Django/Python
- 02说明模式的适用与不适用情境
- 03比较方案及其取舍
- 04提供反模式目录与选择步骤
适用场景
- 01为具体设计问题选择模式
- 02按项目约束比较实现方案
- 03检查代码中的常见反模式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ waybarrios/ opencode-power-pack --skill design-patterns安装后在 Claude Code 中输入/design-patterns确认已加载。
详细介绍
设计模式
针对常见技术栈量身定制的模式建议。每条建议都包含适用场景、不适用场景和权衡取舍。
快速入门
选择模式
- 明确问题: 你要解决什么设计难题?
- 考虑上下文: 使用什么语言?规模如何?变更频率有多高?
- 评估选项: 列出 2-3 种候选模式及其权衡取舍
- 给出建议: 选择最适合当前具体上下文的模式(而不是一般意义上的“最佳模式”)
黄金法则: 模式是工具,不是目标。使用能解决问题的最简单模式。如果没有合适的模式,普通函数或结构体就可以。
Rust 模式
Newtype 模式
封装基础类型,增加类型安全性和领域含义。
struct UserId(u64);
struct OrderId(u64);
// Now the compiler prevents mixing up UserId and OrderId
fn get_order(user: UserId, order: OrderId) -> Order { ... }
适用场景: 两个值的底层类型相同,但含义不同。 不适用场景: 仅使用一次,类型别名就足够。
Builder 模式
逐步构造复杂对象。
let config = ServerConfig::builder()
.host("localhost")
.port(8080)
.max_connections(100)
.build()?;
适用场景: 字段众多且包含许多可选字段的结构体,或构造逻辑复杂的对象。
不适用场景: 只有 1-3 个必需字段的结构体——直接使用 new()。
Typestate 模式
在类型系统中编码状态转换,让无效状态无法表示。
struct Connection<S: State> { /* ... */ state: PhantomData<S> }
struct Disconnected;
struct Connected;
struct Authenticated;
impl Connection<Disconnected> {
fn connect(self) -> Result<Connection<Connected>> { ... }
}
impl Connection<Connected> {
fn authenticate(self, creds: &Credentials) -> Result<Connection<Authenticated>> { ... }
}
impl Connection<Authenticated> {
fn query(&self, sql: &str) -> Result<Rows> { ... }
}
// Can't call query() on a Disconnected connection — compile error
适用场景: 状态转换明确的协议(连接、工作流、解析阶段)。 不适用场景: 简单的开关状态——使用布尔值或枚举更清晰。
Trait 对象与泛型
| 方式 | 分发方式 | 二进制大小 | 灵活性 |
|---|---|---|---|
impl Trait(泛型) | 静态(单态化) | 较大(每种类型一份) | 编译时已知 |
dyn Trait(Trait 对象) | 动态(虚函数表) | 较小(一份) | 运行时可扩展 |
使用泛型的场景: 性能很重要、类型在编译时已知,或需要零成本抽象。 使用 Trait 对象的场景: 需要异构集合、插件系统,或类型集合不固定。
错误处理模式
| 模式 | 适用场景 | Crate |
|---|---|---|
thiserror | 库错误——结构化且包含具体变体 | thiserror |
anyhow | 应用错误——上下文丰富,便于快速传播 | anyhow |
| 自定义枚举 | 调用方需要进行穷尽匹配 | 仅使用 std |
经验法则: 库使用 thiserror(调用方需要匹配错误)。应用使用 anyhow(调用方需要上下文)。
TypeScript / React 模式
复合组件
通过共享上下文隐式协作的组件。
<Select>
<Select.Trigger>Choose a fruit</Select.Trigger>
<Select.Options>
<Select.Option value="apple">Apple</Select.Option>
<Select.Option value="banana">Banana</Select.Option>
</Select.Options>
</Select>
适用场景: 包含多个相关部分的复杂 UI 组件(菜单、折叠面板、标签页)。 不适用场景: 只有 1-2 个元素的简单组件。
自定义 Hook 组合
将行为提取并组合为可复用的 Hook。
function useDebounce<T>(value: T, delay: number): T { ... }
function usePagination(items: Item[], pageSize: number) { ... }
function useLocalStorage<T>(key: string, initial: T) { ... }
适用场景: 组件间共享的有状态逻辑,或组件内部复杂的状态管理。 不适用场景: 不会重复使用的一次性逻辑。
判别联合类型
使用 TypeScript 的联合类型进行类型安全的状态建模。
type AsyncState<T> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; error: Error };
// Exhaustive switch — TypeScript ensures all cases handled
function render(state: AsyncState<User>) {
switch (state.status) {
case "idle": return <Placeholder />;
case "loading": return <Spinner />;
case "success": return <UserCard user={state.data} />;
case "error": return <ErrorBanner error={state.error} />;
}
}
适用场景: 状态机、API 响应状态、表单状态,以及任何包含不同模式的场景。 不适用场景: 简单开关状态使用布尔标记即可。
状态管理决策树
- UI 局部状态? ->
useState/useReducer - 兄弟组件间共享? -> 将状态提升到共同的父组件
- 远距离组件间共享? -> React Context
- 状态逻辑复杂? ->
useReducer+ Context - 服务端状态(API 数据)? -> TanStack Query / SWR
- 全局应用状态? -> Zustand / Jotai(新项目优先于 Redux)
Django / Python 模式
服务层
将业务逻辑与视图和模型分离。
# services/order_service.py
class OrderService:
@staticmethod
def create_order(user: User, items: list[CartItem]) -> Order:
"""Business logic lives here, not in the view."""
order = Order.objects.create(user=user, total=calculate_total(items))
OrderItem.objects.bulk_create([...])
send_confirmation_email(user, order)
return order
适用场景: 涉及多个模型、外部调用或复杂验证的业务逻辑。 不适用场景: 除 Django 内置验证外没有其他业务规则的简单 CRUD。
仓储模式(QuerySet 抽象)
自定义管理器和查询集,用于封装查询逻辑。
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status="published")
class Article(models.Model):
objects = models.Manager() # Default
published = PublishedManager() # Article.published.all()
适用场景: 复杂或可复用的查询逻辑、特定领域的筛选。 不适用场景: 一次性查询或简单筛选。
信号与显式调用
| 方式 | 优点 | 缺点 |
|---|---|---|
| Django 信号 | 解耦,可跨应用使用 | 控制流隐蔽,难以调试,存在顺序问题 |
| 显式调用 | 清晰、易于调试和测试 | 模块之间存在耦合 |
经验法则: 对于真正横切的关注点(审计日志记录、缓存失效),使用信号。对于需要理解并测试流程的业务关键逻辑,使用显式调用。
反模式(适用于所有技术栈)
| 反模式 | 问题 | 改进方法 |
|---|---|---|
| 过早抽象 | 尚未看到模式重复就进行抽象 | 等出现 3 个实例后再提取 |
| 继承优于组合 | 继承层级过深,基类脆弱 | 优先使用组合(特征、钩子、混入) |
| 上帝对象 | 一个类或模块包揽所有工作 | 按职责拆分(SRP) |
| 霰弹式修改 | 一处改动需要编辑许多文件 | 将相关代码归到一起 |
| 功能迷恋 | 某个函数使用其他类的数据多于自身数据 | 将该函数移到它所迷恋的类中 |
| 臆测性通用化 | 为可能永远不会出现的用例构建功能 | YAGNI——立足当前需求构建,必要时再重构 |
| 金锤子 | 所有问题都使用同一种模式或工具 | 根据任务选择合适的工具 |
模式选择指南
当被问到“我该使用哪种模式?”时:
- 描述问题(而不是你认为适用的模式)
- 明确约束条件(性能、可扩展性、团队规模、时间安排)
- 提供 2-3 种方案并说明权衡
- 推荐一种方案并给出明确理由
- 说明何时重新评估——“如果 X 发生变化,就重新考虑 Y”
集成
交叉参考此资源包的 code-architect 技能,以评估结构和架构;参考 code-quality,了解促使重构并采用某种模式的复杂度阈值。