关于 solid-principles
用于在 Java 代码中审查和应用 SOLID 原则的技能。它通过说明五项原则、违规迹象和常见修复方式,支持类设计审查与重构。
主要功能
- 01讲解全部五项 SOLID 原则
- 02列出设计违规迹象
- 03提供按原则分类的审查问题
- 04将常见违规对应到重构模式
适用场景
- 01审查 Java 类设计
- 02重构职责过多的类
- 03检查继承、接口和依赖关系
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ decebals/ claude-code-java --skill solid-principles安装后在 Claude Code 中输入/solid-principles确认已加载。
附带文件README.md
详细介绍
SOLID 原则技能
审查并在 Java 代码中应用 SOLID 原则。
适用场景
- 用户说“检查 SOLID”/“SOLID 审查”/“这个类是不是做得太多了?”
- 审查类设计
- 重构大型类
- 关注设计的代码审查
快速参考
| 字母 | 原则 | 一句话说明 |
|---|---|---|
| S | 单一职责原则 | 一个类 = 一个变更理由 |
| O | 开闭原则 | 对扩展开放,对修改关闭 |
| L | 里氏替换原则 | 子类型必须能够替换基类型 |
| I | 接口隔离原则 | 多个专用接口 > 一个通用接口 |
| D | 依赖倒置原则 | 依赖抽象,而非具体实现 |
S - 单一职责原则(SRP)
“一个类应该只有一个变更理由。”
违反示例
// ❌ BAD: UserService does too much
public class UserService {
public User createUser(String name, String email) {
// validation logic
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
// persistence logic
User user = new User(name, email);
entityManager.persist(user);
// notification logic
String subject = "Welcome!";
String body = "Hello " + name;
emailClient.send(email, subject, body);
// audit logic
auditLog.log("User created: " + email);
return user;
}
}
问题:
- 验证逻辑变更?修改 UserService
- 邮件模板变更?修改 UserService
- 审计格式变更?修改 UserService
- 难以分别测试每项职责
重构后
// ✅ GOOD: Each class has one responsibility
public class UserValidator {
public void validate(String name, String email) {
if (email == null || !email.contains("@")) {
throw new ValidationException("Invalid email");
}
}
}
public class UserRepository {
public User save(User user) {
entityManager.persist(user);
return user;
}
}
public class WelcomeEmailSender {
public void sendWelcome(User user) {
String subject = "Welcome!";
String body = "Hello " + user.getName();
emailClient.send(user.getEmail(), subject, body);
}
}
public class UserAuditLogger {
public void logCreation(User user) {
auditLog.log("User created: " + user.getEmail());
}
}
public class UserService {
private final UserValidator validator;
private final UserRepository repository;
private final WelcomeEmailSender emailSender;
private final UserAuditLogger auditLogger;
public User createUser(String name, String email) {
validator.validate(name, email);
User user = repository.save(new User(name, email));
emailSender.sendWelcome(user);
auditLogger.logCreation(user);
return user;
}
}
如何检测违反 SRP 的情况
- 类中有许多来自不同领域的
import语句 - 类名包含“And”“Manager”或“Handler”(通常如此)
- 方法处理互不相关的数据
- 某个领域的变更需要修改无关的方法
- 难以为类取一个简洁的名称
快速检查问题
- 能否用一句不含“和”的话描述这个类的用途?
- 是否会有不同的利益相关者要求修改这个类?
- 是否有方法未使用类的大部分字段?
O - 开闭原则(OCP)
“软件实体应该对扩展开放,对修改关闭。”
违反示例
// ❌ BAD: Must modify class to add new discount type
public class DiscountCalculator {
public double calculate(Order order, String discountType) {
if (discountType.equals("PERCENTAGE")) {
return order.getTotal() * 0.1;
} else if (discountType.equals("FIXED")) {
return 50.0;
} else if (discountType.equals("LOYALTY")) {
return order.getTotal() * order.getCustomer().getLoyaltyRate();
}
// Every new discount type = modify this class
return 0;
}
}
重构后
// ✅ GOOD: Add new discounts without modifying existing code
public interface DiscountStrategy {
double calculate(Order order);
boolean supports(String discountType);
}
public class PercentageDiscount implements DiscountStrategy {
@Override
public double calculate(Order order) {
return order.getTotal() * 0.1;
}
@Override
public boolean supports(String discountType) {
return "PERCENTAGE".equals(discountType);
}
}
public class FixedDiscount implements DiscountStrategy {
@Override
public double calculate(Order order) {
return 50.0;
}
@Override
public boolean supports(String discountType) {
return "FIXED".equals(discountType);
}
}
public class LoyaltyDiscount implements DiscountStrategy {
@Override
public double calculate(Order order) {
return order.getTotal() * order.getCustomer().getLoyaltyRate();
}
@Override
public boolean supports(String discountType) {
return "LOYALTY".equals(discountType);
}
}
// New discount? Just add new class, no modification needed
public class SeasonalDiscount implements DiscountStrategy {
@Override
public double calculate(Order order) {
return order.getTotal() * 0.2;
}
@Override
public boolean supports(String discountType) {
return "SEASONAL".equals(discountType);
}
}
public class DiscountCalculator {
private final List<DiscountStrategy> strategies;
public DiscountCalculator(List<DiscountStrategy> strategies) {
this.strategies = strategies;
}
public double calculate(Order order, String discountType) {
return strategies.stream()
.filter(s -> s.supports(discountType))
.findFirst()
.map(s -> s.calculate(order))
.orElse(0.0);
}
}
如何检测违反 OCP 的情况
- 按类型/状态进行的
if/else或switch分支会不断增加 - 基于枚举的分派逻辑经常需要添加新值
- 变更需要修改核心类
常见 OCP 模式
| 模式 | 适用场景 |
|---|---|
| 策略模式 | 同一操作有多种算法 |
| 模板方法模式 | 结构相同,步骤不同 |
| 装饰器模式 | 动态添加行为 |
| 工厂模式 | 无需指定类即可创建对象 |
L - 里氏替换原则(LSP)
“子类型必须能够替换基类型。”
违反示例
// ❌ BAD: Square violates Rectangle contract
public class Rectangle {
protected int width;
protected int height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getArea() {
return width * height;
}
}
public class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // Violates expected behavior!
}
@Override
public void setHeight(int height) {
this.width = height; // Violates expected behavior!
this.height = height;
}
}
// This test fails for Square!
void testRectangle(Rectangle r) {
r.setWidth(5);
r.setHeight(4);
assert r.getArea() == 20; // Square returns 16!
}
重构后
// ✅ GOOD: Separate abstractions
public interface Shape {
int getArea();
}
public class Rectangle implements Shape {
private final int width;
private final int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public int getArea() {
return width * height;
}
}
public class Square implements Shape {
private final int side;
public Square(int side) {
this.side = side;
}
@Override
public int getArea() {
return side * side;
}
}
LSP 规则
| 规则 | 含义 |
|---|---|
| 前置条件 | 子类不能加强前置条件(要求更多) |
| 后置条件 | 子类不能削弱后置条件(承诺更少) |
| 不变式 | 子类必须维持父类的不变式 |
| 历史约束 | 子类不能意外修改继承的状态 |
如何检测违反 LSP 的情况
- 子类抛出父类不会抛出的异常
- 父类返回对象时,子类返回 null
- 子类意外忽略或覆盖父类行为
- 调用方法前进行
instanceof检查 - 接口方法的实现为空或抛出异常
快速检查
// If you see this, LSP might be violated
if (bird instanceof Penguin) {
// don't call fly()
} else {
bird.fly();
}
I - 接口隔离原则(ISP)
“客户端不应被迫依赖自己不会使用的接口。”
违反示例
// ❌ BAD: Fat interface forces unnecessary implementations
public interface Worker {
void work();
void eat();
void sleep();
void attendMeeting();
void writeReport();
}
// Robot can't eat or sleep!
public class Robot implements Worker {
@Override public void work() { /* OK */ }
@Override public void eat() { /* Can't eat! */ }
@Override public void sleep() { /* Can't sleep! */ }
@Override public void attendMeeting() { /* OK */ }
@Override public void writeReport() { /* Maybe */ }
}
// Intern doesn't attend meetings or write reports
public class Intern implements Worker {
@Override public void work() { /* OK */ }
@Override public void eat() { /* OK */ }
@Override public void sleep() { /* OK */ }
@Override public void attendMeeting() { /* Not allowed! */ }
@Override public void writeReport() { /* Not expected! */ }
}
重构后
// ✅ GOOD: Segregated interfaces
public interface Workable {
void work();
}
public interface Feedable {
void eat();
void sleep();
}
public interface Manageable {
void attendMeeting();
void writeReport();
}
// Combine what you need
public class Employee implements Workable, Feedable, Manageable {
@Override public void work() { /* ... */ }
@Override public void eat() { /* ... */ }
@Override public void sleep() { /* ... */ }
@Override public void attendMeeting() { /* ... */ }
@Override public void writeReport() { /* ... */ }
}
public class Robot implements Workable {
@Override public void work() { /* ... */ }
// No unnecessary methods!
}
public class Intern implements Workable, Feedable {
@Override public void work() { /* ... */ }
@Override public void eat() { /* ... */ }
@Override public void sleep() { /* ... */ }
// No meeting/report methods!
}
如何检测违反 ISP 的情况
- 实现中包含空方法或
throw new UnsupportedOperationException() - 接口有 10 个以上的方法
- 不同客户端使用的方法子集完全不同
- 接口变更会影响无关的实现
Java 标准库中的违反示例
// java.util.List has many methods - but this is acceptable for collections
// However, be careful with your own interfaces!
// ❌ This interface is too fat for most use cases
public interface Repository<T> {
T findById(Long id);
List<T> findAll();
T save(T entity);
void delete(T entity);
void deleteById(Long id);
List<T> findByExample(T example);
Page<T> findAll(Pageable pageable);
List<T> findAllById(Iterable<Long> ids);
long count();
boolean existsById(Long id);
// ... 20 more methods
}
// ✅ Better: Split by use case
public interface ReadRepository<T> {
Optional<T> findById(Long id);
List<T> findAll();
}
public interface WriteRepository<T> {
T save(T entity);
void delete(T entity);
}
D - 依赖倒置原则(DIP)
“高层模块不应依赖低层模块。两者都应依赖抽象。”
违反示例
// ❌ BAD: High-level depends on low-level directly
public class OrderService {
private MySqlOrderRepository repository; // Concrete class!
private SmtpEmailSender emailSender; // Concrete class!
public OrderService() {
this.repository = new MySqlOrderRepository(); // Hard dependency
this.emailSender = new SmtpEmailSender(); // Hard dependency
}
public void createOrder(Order order) {
repository.save(order);
emailSender.send(order.getCustomerEmail(), "Order confirmed");
}
}
问题:
- 无法在没有真实 MySQL 数据库的情况下测试
- 无法替换邮件服务提供商
- OrderService 了解 MySQL、SMTP 的细节
重构后
// ✅ GOOD: Depend on abstractions
// Abstractions (interfaces)
public interface OrderRepository {
void save(Order order);
Optional<Order> findById(Long id);
}
public interface NotificationSender {
void send(String recipient, String message);
}
// High-level module depends on abstractions
public class OrderService {
private final OrderRepository repository;
private final NotificationSender notificationSender;
// Dependencies injected
public OrderService(OrderRepository repository,
NotificationSender notificationSender) {
this.repository = repository;
this.notificationSender = notificationSender;
}
public void createOrder(Order order) {
repository.save(order);
notificationSender.send(order.getCustomerEmail(), "Order confirmed");
}
}
// Low-level modules implement abstractions
public class MySqlOrderRepository implements OrderRepository {
@Override
public void save(Order order) { /* MySQL specific */ }
@Override
public Optional<Order> findById(Long id) { /* MySQL specific */ }
}
public class SmtpEmailSender implements NotificationSender {
@Override
public void send(String recipient, String message) { /* SMTP specific */ }
}
// Easy to test with mocks!
public class InMemoryOrderRepository implements OrderRepository {
private Map<Long, Order> orders = new HashMap<>();
@Override
public void save(Order order) {
orders.put(order.getId(), order);
}
@Override
public Optional<Order> findById(Long id) {
return Optional.ofNullable(orders.get(id));
}
}
使用 Spring 实现 DIP
// Spring handles dependency injection automatically
@Service
public class OrderService {
private final OrderRepository repository;
private final NotificationSender notificationSender;
// Constructor injection (recommended)
public OrderService(OrderRepository repository,
NotificationSender notificationSender) {
this.repository = repository;
this.notificationSender = notificationSender;
}
}
@Repository
public class JpaOrderRepository implements OrderRepository {
// Spring provides implementation
}
@Component
@Profile("production")
public class SmtpEmailSender implements NotificationSender { }
@Component
@Profile("test")
public class MockEmailSender implements NotificationSender { }
如何检测 DIP 违规
- 业务逻辑中出现
new ConcreteClass() - 导入语句包含实现包(例如
com.mysql、org.apache.http) - 难以轻松替换实现
- 测试需要真实基础设施(数据库、网络)
SOLID 审查清单
审查代码时,请检查:
| 原则 | 问题 |
|---|---|
| SRP | 这个类是否有多个变更原因? |
| OCP | 添加新类型/功能是否需要修改这个类? |
| LSP | 在需要父类的任何地方,是否都可以使用子类? |
| ISP | 是否存在空方法实现或抛出异常的方法实现? |
| DIP | 高层代码是否依赖具体实现? |
常见重构模式
| 违规 | 重构 |
|---|---|
| SRP - 上帝类 | 提取类、移动方法 |
| OCP - 类型切换 | 策略模式、工厂 |
| LSP - 受损的继承关系 | 组合优于继承、提取接口 |
| ISP - 臃肿的接口 | 拆分接口、角色接口 |
| DIP - 硬依赖 | 依赖注入、抽象工厂 |
相关技能
design-patterns- 实现模式(工厂、策略、观察者等)clean-code- 代码级原则(DRY、KISS、命名)java-code-review- 综合审查清单
评论
支持 Markdown