原文: https://howtodoinjava.com/design-patterns/single-responsibility-principle/

单一责任原则(SRP)指出,软件组件(通常为类)必须仅具有一个职责。 类负有全部责任这一事实意味着,类只负责一件具体的事情,因此,我们可以得出结论,类只有一个改变的理由。 它是 5 个著名的 SOLID 原则之一。

一个类只有一个改变的理由。

还有另一种看待这一原则的方法。 如果在查看一个类时,我们发现相互排斥且彼此不相关的方法,则它们是必须分解为较小类的不同职责。 请记住,较小的类总是更好。

1.动机

如果出于各种原因必须对类进行修改,则意味着抽象不正确,并且该类承担了太多责任。 基本上,我们希望在所有情况下都避免具有多个职责的对象(通常称为神对象,因为它们知道的太多或超出了他们应有的知识)。 这些对象将不同的行为(大多数是不相关的)组合在一起,从而使它们难以维护。

2.单一责任原则示例

要了解单责任原则的真实示例,我们可以看一下 JDK 代码和其他流行的库,例如 Log4J,Spring 框架等。Log4J 代码具有使用日志记录方法的不同类,不同的类是日志记录级别等等。 在 Spring 框架中,类实际上很小,通常只执行一两个相关操作。

让我们再举一个自己的例子,以更好地理解什么是单一责任原则。

2.1 问题

为了理解 SRP 原则,我们假设我们正在开发涉及与员工一起工作的应用。 我们有一个接口IEmployeeStore,它的实现EmployeeStore具有以下方法。

  1. public interface IEmployeeStore
  2. {
  3. public Employee getEmployeeById(Long id);
  4. public void addEmployee(Employee employee);
  5. public void sendEmail(Employee employee, String content);
  6. }
  1. public class EmployeeStore implements IEmployeeStore
  2. {
  3. @Override
  4. public Employee getEmployeeById(Long id) {
  5. return null;
  6. }
  7. @Override
  8. public void addEmployee(Employee employee) {
  9. }
  10. @Override
  11. public void sendEmail(Employee employee, String content) {
  12. }
  13. }

在任何普通应用上,上述类似乎都不错。 使用EmployeeStore,可以获取/添加员工并向他们发送电子邮件。

现在假设产品发布后,我们要求电子邮件内容可以是两种类型,即 HTML 和文本。 该类支持仅文本内容。 你会做什么?

解决此问题的一种方法是创建另一种方法sendHtmlEmail(),但是当我们被要求支持为两种内容类型发送电子邮件的不同协议时会发生什么。 整体类看起来非常丑陋,难以阅读和维护。

而且总是有机会在修改期间,某些开发人员可以更改用于共享员工方法的获取/添加员工方法的逻辑。

2.2 SRP 原则的解决方案

要解决此问题,我们必须退出电子邮件功能,以分离专门处理电子邮件相关功能的接口和类。 这样,我们可以确定其他功能不会受到影响。

基于我们的假设,我们可以抽象出两个接口IEmailSenderIEmailContent。 第一个接口负责电子邮件的发送过程,第二个接口负责传递电子邮件的内容及其类型。

重构的类在下面给出。

  1. public interface IEmployeeStore
  2. {
  3. public Employee getEmployeeById(Long id);
  4. public void addEmployee(Employee employee);
  5. }
  1. public class EmployeeStore implements IEmployeeStore
  2. {
  3. //inject in runtime
  4. private IEmailSender emailSender;
  5. @Override
  6. public Employee getEmployeeById(Long id) {
  7. return null;
  8. }
  9. @Override
  10. public void addEmployee(Employee employee) {
  11. }
  12. }
  1. public interface IEmailSender
  2. {
  3. public void sendEmail(Employee employee, IEmailContent content);
  4. }
  1. public class EmailSender implements IEmailSender
  2. {
  3. @Override
  4. public void sendEmail(Employee employee, IEmailContent content) {
  5. //logic
  6. }
  7. }
  1. public interface IEmailContent {
  2. }
  1. public class EmailContent implements IEmailContent
  2. {
  3. private String type;
  4. private String content;
  5. }

现在,如果要更改电子邮件功能,我们将仅更改EmailSender类。 员工 CRUD 操作的任何更改将仅在EmployeeStore中发生。 一种能力的任何改变都不会错误地改变另一种能力。 它们也不太容易阅读和维护。

3.好处

3.1 易于理解和维护

当类仅做“一件事情”时,其接口通常具有较少数量的方法(和成员变量),这些方法很容易解释。 它使代码更易于阅读和理解。

当需要更改应用行为时,与类职责相关的更改是相当隔离的。 它减少了破坏软件其他无关区域的机会。 它使代码更易于维护。

3.2 改善的可用性

如果一个类有多个职责,并且为了某个职责而需要在应用的其他部分中使用它,则可能不利地暴露其他职责,而这是不希望的。 它可能导致应用中不良行为,例如,安全和数据隐私问题。

如果该类严格遵循 SRP 原则,则不会暴露出不必要的功能,这将使该类更加可用,而不必担心会产生不利影响。

4. 结论

单责任原则设计模式对代码的适应性具有巨大的积极影响。 与不遵循该原则的等效代码相比,符合 SRP 的代码导致产生了更多的类,这些类更小且指向范围更广。

SRP 的实现主要是通过抽象接口后的代码并将不相关功能的职责委托给运行时恰好在接口后的实现来实现的。

阅读更多:

关注点分离