hymdbk

导航

Java--单一职责原则

单一职责原则(SRP):

一、核心定义

一个类或一个方法只负责一件事,只有一个引起它变化的原因。各司其职,不混合职责。

二、反面示例(违反单一职责)

以下代码中,User 类同时做了三件事:用户业务、文件保存、日志打印。

// 违反SRP:一个类干三件事
class User {
// 职责1:用户业务
public void setUserInfo(String name, int age) {
System.out.println("设置用户信息:" + name + ", " + age);
}

// 职责2:文件读写
public void saveFile(String content) {
System.out.println("保存文件:" + content);
}

// 职责3:日志输出
public void printLog(String msg) {
System.out.println("[LOG] " + msg);
}
}

// 测试代码
public class BadExample {
public static void main(String[] args) {
User user = new User();
user.setUserInfo("张三", 25);
user.saveFile("用户数据");
user.printLog("操作完成");
}
}


问题:修改日志逻辑、文件逻辑或用户逻辑都要改动同一个类,耦合度高,容易出bug。

三、正面示例(遵守单一职责)

拆分三个类,每个类只做一件事。

// 职责1:只负责用户业务
class UserInfo {
    public void setUserInfo(String name, int age) {
        System.out.println("设置用户信息:" + name + ", " + age);
    }
}

// 职责2:只负责文件存储
class FileUtil {
    public void saveFile(String content) {
        System.out.println("保存文件:" + content);
    }
}

// 职责3:只负责日志打印
class LogUtil {
    public void printLog(String msg) {
        System.out.println("[LOG] " + msg);
    }
}

// 测试代码
public class GoodExample {
    public static void main(String[] args) {
        UserInfo userInfo = new UserInfo();
        FileUtil fileUtil = new FileUtil();
        LogUtil logUtil = new LogUtil();

        userInfo.setUserInfo("李四", 30);
        fileUtil.saveFile("用户数据");
        logUtil.printLog("操作完成");
    }
}

四、完整重构案例(订单系统)

4.1 违反SRP的订单类

// 一个类做了太多事:计算价格、保存数据库、发送邮件、记录日志
class BadOrder {
private String orderId;
private double amount;

public BadOrder(String orderId, double amount) {
this.orderId = orderId;
this.amount = amount;
}

// 职责1:计算价格
public double calculateTotal() {
return amount * 0.9; // 打折
}

// 职责2:保存到数据库
public void saveToDatabase() {
System.out.println("保存订单 " + orderId + " 到数据库");
}

// 职责3:发送邮件
public void sendEmail() {
System.out.println("发送订单确认邮件");
}

// 职责4:记录日志
public void log(String msg) {
System.out.println("[ORDER LOG] " + msg);
}
}


4.2 遵守SRP的重构版本

// 职责1:订单实体(只负责数据存储)
class Order {
    private String orderId;
    private double amount;

    public Order(String orderId, double amount) {
        this.orderId = orderId;
        this.amount = amount;
    }

    public String getOrderId() { return orderId; }
    public double getAmount() { return amount; }
}

// 职责2:价格计算器
class PriceCalculator {
    public double calculateTotal(Order order) {
        return order.getAmount() * 0.9; // 打折
    }
}

// 职责3:数据库持久化
class OrderRepository {
    public void save(Order order) {
        System.out.println("保存订单 " + order.getOrderId() + " 到数据库");
    }
}

// 职责4:邮件服务
class EmailService {
    public void sendOrderConfirmation(Order order) {
        System.out.println("发送订单确认邮件,订单号:" + order.getOrderId());
    }
}

// 职责5:日志服务
class Logger {
    public void log(String msg) {
        System.out.println("[LOG] " + msg);
    }
}

// 业务编排层(组合各职责)
class OrderService {
    private PriceCalculator calculator = new PriceCalculator();
    private OrderRepository repository = new OrderRepository();
    private EmailService emailService = new EmailService();
    private Logger logger = new Logger();

    public void processOrder(Order order) {
        double total = calculator.calculateTotal(order);
        logger.log("计算总价:" + total);

        repository.save(order);
        logger.log("订单已保存");

        emailService.sendOrderConfirmation(order);
        logger.log("邮件已发送");
    }
}

// 测试代码
public class RefactoredOrderSystem {
    public static void main(String[] args) {
        Order order = new Order("ORD-001", 100.0);
        OrderService service = new OrderService();
        service.processOrder(order);
    }
}

五、判断方法(一句话测试)

用一句话描述类的职责,如果出现“并且”、“同时”、“以及”等连接词,通常违反SRP。

· 错误示例:「订单类负责计算价格并且保存到数据库并且发送邮件」
· 正确示例:「价格计算器负责计算订单总价」

六、SRP的好处总结

· 代码易维护、易修改
· 改动互不影响,降低bug率
· 高内聚低耦合,便于单元测试
· 职责清晰,符合面向对象设计规范

posted on 2026-05-10 18:51  忽有眠  阅读(7)  评论(0)    收藏  举报