关于 solid
讲解面向对象代码中五项 SOLID 原则的软件设计 Skill。帮助开发者诊断可维护性与扩展性问题,并选择有针对性的设计调整。
主要功能
- 01单一职责原则指导
- 02开闭原则的扩展指导
- 03里氏替换原则检查
- 04接口隔离原则指导
- 05依赖倒置原则指导
- 06原则间关系与常见误解
适用场景
- 01诊断职责过多的类
- 02设计可扩展现有行为的功能
- 03检查继承关系与接口边界
- 04减少对具体实现细节的依赖
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ krzysztofsurdy/ code-virtuoso --skill solid安装后在 Claude Code 中输入/solid确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/dip.mdisp.mdlsp.mdocp.mdsrp.md详细介绍
SOLID 原则
面向对象设计的五项核心原则,能让系统更易于维护、测试和扩展。Robert C. Martin(Uncle Bob)系统阐述了这些理念,Michael Feathers 则创造了 SOLID 这个缩写。
原则索引
| 原则 | 概述 | 参考 |
|---|---|---|
| S:单一职责 | 一个类应该只有一个变更理由 | 参考 |
| O:开闭原则 | 对扩展开放,对修改关闭 | 参考 |
| L:里氏替换 | 子类型必须能够替代其基类型 | 参考 |
| I:接口隔离 | 倾向于使用多个特定接口,而不是一个通用接口 | 参考 |
| D:依赖反转 | 依赖抽象,而非具体实现 | 参考 |
SOLID 为什么重要
没有 SOLID,代码库会逐渐出现以下问题:
- 僵化:一处变更会波及许多无关模块
- 脆弱:修改某个区域会导致另一个看似无关的区域出错
- 难以复用:组件彼此交织得太紧密,难以将其提取出来复用
- 黏滞性:正确实现比绕过设计走捷径更困难
SOLID 通过清晰的边界、明确的契约和定义好的扩展点来解决这些问题。
快速决策指南
| 症状 | 可能违反的原则 | 修复方法 |
|---|---|---|
| 类承担的职责过多 | SRP | 拆分为职责明确的类 |
| 添加功能需要修改现有类 | OCP | 引入多态或策略 |
| 子类被用来替代父类时出现问题 | LSP | 修正继承层次结构或改用组合 |
| 类被迫实现未使用的方法 | ISP | 将接口拆分为更小的接口 |
| 高层模块导入底层细节 | DIP | 引入抽象层 |
快速示例
SRP:改造前后
// BEFORE: Class handles both user data AND email sending
class UserService {
public function createUser(string $name, string $email): void { /* ... */ }
public function sendWelcomeEmail(string $email): void { /* ... */ }
}
// AFTER: Each class has one responsibility
class UserService {
public function __construct(private UserNotifier $notifier) {}
public function createUser(string $name, string $email): void {
// persist user...
$this->notifier->welcomeNewUser($email);
}
}
class UserNotifier {
public function welcomeNewUser(string $email): void { /* ... */ }
}
OCP:扩展而不修改
interface DiscountPolicy {
public function calculate(float $total): float;
}
class PercentageDiscount implements DiscountPolicy {
public function __construct(private float $rate) {}
public function calculate(float $total): float {
return $total * $this->rate;
}
}
// Adding a new discount type requires NO changes to existing code
class FlatDiscount implements DiscountPolicy {
public function __construct(private float $amount) {}
public function calculate(float $total): float {
return min($this->amount, $total);
}
}
DIP:依赖抽象
// High-level policy depends on abstraction, not on database details
interface OrderRepository {
public function save(Order $order): void;
}
class PlaceOrderHandler {
public function __construct(private OrderRepository $repository) {}
public function handle(PlaceOrderCommand $cmd): void {
$order = Order::create($cmd->items);
$this->repository->save($order);
}
}
原则之间的关系
这五项原则相互促进:
- SRP + ISP:两者都将类或接口的职责限定在单一关注点上
- OCP + DIP:抽象使无需修改即可扩展成为可能
- LSP + OCP:只有子类型能够正确替换基类型,多态扩展才有效
- ISP + DIP:良好隔离的接口让依赖恰当的抽象变得更容易
常见误解
- “一个类一个方法”:SRP 指的是只有一个变更理由,而不是只能有一个方法。只要方法都服务于同一职责,一个类可以有许多方法。
- “绝不修改现有代码”:OCP 不禁止修复缺陷。它的意思是,添加新行为时应无需修改现有的可运行代码。
- “始终使用接口”:DIP 要求依赖抽象。有时基类就是恰当的抽象。对于永远不会有第二种实现的类,不要创建接口。
- “继承不好”:LSP 并不反对继承。它为正确的继承设定规则,确保子类型始终可以替换基类型。
最佳实践
- 逐步采用 SOLID。不要一次性重构所有内容
- 将 SOLID 视为诊断工具:当代码难以修改时,检查违反了哪项原则
- 配合设计模式使用:Strategy(OCP)、Adapter(DIP)和 Decorator(OCP)都是 SOLID 理念的直接实现
- 先编写测试。TDD 会引导设计符合 SOLID 原则
- 使用语言提供的严格类型和不可变特性(readonly 或 final 字段、不可变值类型、枚举或密封类型),让编译器强制执行设计约束
评论
支持 Markdown