Single Responsibility Principle in Java – Java中的单一责任原则

最后修改: 2020年 5月 3日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll be discussing the Single Responsibility Principle, as one of the SOLID principles of object-oriented programming.

在本教程中,我们将讨论单一责任原则,作为面向对象编程的SOLID原则之一。

Overall, we’ll go in-depth on what this principle is and how to implement it when designing our software. Furthermore, we’ll explain when this principle can be misleading.

总的来说,我们将深入探讨这个原则是什么,以及在设计我们的软件时如何实现它。此外,我们还将解释这个原则在什么时候会产生误导。

*SRP = Single Responsibility Principle

*SRP=单一责任原则

2. Single Responsibility Principle

2.单一责任原则

As the name suggests, this principle states that each class should have one responsibility, one single purpose. This means that a class will do only one job, which leads us to conclude it should have only one reason to change.

顾名思义,这一原则指出,每个类应该有一个责任,一个单一的目的。这意味着一个类只做一项工作,这使我们得出结论,它应该有唯一的理由去改变

We don’t want objects that know too much and have unrelated behavior. These classes are harder to maintain. For example, if we have a class that we change a lot, and for different reasons, then this class should be broken down into more classes, each handling a single concern. Surely, if an error occurs, it will be easier to find.

我们不希望对象知道的太多,而且有不相关的行为。这些类更难维护。例如,如果我们有一个类,我们经常改变,而且是出于不同的原因,那么这个类应该被分解成更多的类,每个类处理一个单独的关注。当然,如果发生错误,会更容易发现。

Let’s consider a class that contains code that changes the text in some way. The only job of this class should be manipulating text.

让我们考虑一个包含以某种方式改变文本的代码的类。这个类的唯一工作应该是操纵文本

public class TextManipulator {
    private String text;

    public TextManipulator(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public void appendText(String newText) {
        text = text.concat(newText);
    }
    
    public String findWordAndReplace(String word, String replacementWord) {
        if (text.contains(word)) {
            text = text.replace(word, replacementWord);
        }
        return text;
    }
    
    public String findWordAndDelete(String word) {
        if (text.contains(word)) {
            text = text.replace(word, "");
        }
        return text;
    }

    public void printText() {
        System.out.println(textManipulator.getText());
    }
}

Although this may seem fine, it is not a good example of the SRP. Here we have two responsibilities: manipulating and printing the text.

虽然这看起来不错,但它并不是SRP的一个好例子。这里我们有两个责任操纵和打印文本

Having a method that prints out text in this class violate the Single Responsibility Principle. For this purpose, we should create another class, which will only handle printing text:

在这个类中有一个打印文本的方法,违反了单一责任原则。为此,我们应该创建另一个类,它将只处理打印文本。

public class TextPrinter {
    TextManipulator textManipulator;

    public TextPrinter(TextManipulator textManipulator) {
        this.textManipulator = textManipulator;
    }

    public void printText() {
        System.out.println(textManipulator.getText());
    }

    public void printOutEachWordOfText() {
        System.out.println(Arrays.toString(textManipulator.getText().split(" ")));
    }

    public void printRangeOfCharacters(int startingIndex, int endIndex) {
        System.out.println(textManipulator.getText().substring(startingIndex, endIndex));
    }
}

Now, in this class, we can create methods for as many variations of printing text as we want, because that’s its job.

现在,在这个类中,我们可以为尽可能多的打印文本的变化创建方法,因为这是它的工作。

3. How Can This Principle Be Misleading?

3.这个原则怎么会有误导性?

The trick of implementing SRP in our software is knowing the responsibility of each class.

在我们的软件中实施SRP的诀窍是知道每个类的责任

However, every developer has their vision of the class purpose, which makes things tricky. Since we don’t have strict instructions on how to implement this principle, we are left with our interpretations of what the responsibility will be.

然而,每个开发者都有他们对类目的看法,这使得事情变得很棘手。由于我们没有关于如何实现这一原则的严格指示,我们只能对责任进行解释。

What this means is that sometimes only we, as designers of our application, can decide if something is in the scope of a class or not.

这意味着,有时只有我们,作为应用程序的设计者,才能决定某些东西是否在一个类的范围内。

When writing a class according to the SRP principle, we have to think about the problem domain, business needs, and application architecture. It is very subjective, which makes implementing this principle harder then it seems. It will not be as simple as the example we have in this tutorial.

在根据SRP原则编写类时,我们必须考虑到问题域、业务需求和应用架构。这是非常主观的,这使得实现这一原则比想象中更难。它不会像我们在本教程中的例子那样简单。

This leads us to the next point.

这将我们引向下一个问题。

4. Cohesion

4.凝聚力

Following the SRP principle, our classes will adhere to one functionality. Their methods and data will be concerned with one clear purpose. This means high cohesion, as well as robustness, which together reduce errors.

遵循SRP原则,我们的类将坚持一个功能。它们的方法和数据将与一个明确的目的有关。这意味着高内聚力以及健壮性,它们共同减少错误

When designing software based on the SRP principle, cohesion is essential, since it helps us to find single responsibilities for our classes. This concept also helps us find classes that have more than one responsibility.

在基于SRP原则设计软件时,内聚力是必不可少的,因为它可以帮助我们为我们的类找到单一的责任。这个概念也可以帮助我们找到有一个以上责任的类。

Let’s go back to our TextManipulator class methods:

让我们回到我们的TextManipulator类方法。

...

public void appendText(String newText) {
    text = text.concat(newText);
}

public String findWordAndReplace(String word, String replacementWord) {
    if (text.contains(word)) {
        text = text.replace(word, replacementWord);
    }
    return text;
}

public String findWordAndDelete(String word) {
    if (text.contains(word)) {
        text = text.replace(word, "");
    }
    return text;
}

...

Here, we have a clear representation of what this class does: Text manipulation.

在这里,我们对这个类的作用有一个清晰的表述。文本操作。

But, if we don’t think about cohesion and we don’t have a clear definition of what this class’s responsibility is, we could say that writing and updating the text are two different and separate jobs. Lead by this thought, we can conclude than these should be two separate classes: WriteText and UpdateText.

但是,如果我们不考虑凝聚力,也没有明确定义这个班级的责任是什么,我们可以说,写作和更新文本是两个不同的独立工作。在这种想法的引导下,我们可以得出比这应该是两个独立的类的结论。WriteTextUpdateText

In reality, we’d get two classes that are tightly coupled and loosely cohesive, which should almost always be used together. These three methods may perform different operations, but they essentially serve one single purpose: Text manipulation. The key is not to overthink.

在现实中,我们会得到两个紧密耦合和松散凝聚的类,它们几乎总是应该一起使用。这三个方法可能执行不同的操作,但它们本质上只为一个目的服务:文本操作。关键是不要想太多。

One of the tools that can help achieve high cohesion in methods is LCOM. Essentially, LCOM measures the connection between class components and their relation to one another.

可以帮助实现方法的高内聚力的工具之一是LCOM。从本质上讲,LCOM衡量了类组件之间的联系以及它们彼此之间的关系。

Martin Hitz and Behzad Montazeri introduced LCOM4, which Sonarqube metered for a time, but has since deprecated.

Martin Hitz和Behzad Montazeri介绍了LCOM4Sonarqube曾经计量过一段时间,但后来被废弃。

5. Conclusion

5.总结

Even though the name of the principle is self-explanatory, we can see how easy it is to implement incorrectly. Make sure to distinguish the responsibility of every class when developing a project and pay extra attention to cohesion.

尽管这个原则的名字是不言自明的,但我们可以看到它是多么容易被错误地实施。在开发项目时,一定要区分每一个类的责任,并且要格外注意内聚力。

As always, the code is available on GitHub.

一如既往,代码可在GitHub上获得