1. 概要

このチュートリアルでは、オブジェクト指向プログラミングの SOLID原則の1つとして、単一責任原則について説明します。

全体として、この原則とは何か、およびソフトウェアを設計するときにそれを実装する方法について詳しく説明します。 さらに、この原則が誤解を招く可能性がある場合についても説明します。

*SRP=単一責任の原則

2. 単一責任の原則

名前が示すように、この原則は、各クラスが 1つの責任、1つの単一の目的を持つべきであると述べています。 これは、クラスが1つのジョブのみを実行することを意味します。これにより、変更する理由は1つだけであると結論付けることができます。

あまりにも多くのことを知っていて、無関係な振る舞いをするオブジェクトは必要ありません。 これらのクラスは維持するのが難しいです。 たとえば、私たちが頻繁に変更するクラスがあり、さまざまな理由で、このクラスをより多くのクラスに分割し、それぞれが1つの懸念事項を処理する必要があります。 確かに、エラーが発生した場合は、見つけやすくなります。

何らかの方法でテキストを変更するコードを含むクラスを考えてみましょう。 このクラスの唯一の仕事は、テキストの操作である必要があります。

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());
    }
}

これは問題ないように見えるかもしれませんが、SRPの良い例ではありません。 ここに2つの責任があります:テキストの操作と印刷

このクラスのテキストを出力するメソッドがあると、単一責任の原則に違反します。 この目的のために、テキストの印刷のみを処理する別のクラスを作成する必要があります。

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));
    }
}

さて、このクラスでは、テキストを印刷するためのさまざまなバリエーションのメソッドを作成できます。これがその仕事だからです。

3. この原則はどのように誤解を招く可能性がありますか?

ソフトウェアにSRPを実装する秘訣は、各クラスの責任を知ることです。

ただし、すべての開発者はクラスの目的のビジョンを持っているため、注意が必要です。 この原則をどのように実施するかについての厳密な指示がないため、責任がどうなるかについての解釈が残されています。

これが意味することは、アプリケーションの設計者である私たちだけが、何かがクラスのスコープ内にあるかどうかを判断できる場合があるということです。

SRPの原則に従ってクラスを作成するときは、問題のドメイン、ビジネスニーズ、およびアプリケーションアーキテクチャについて考える必要があります。 これは非常に主観的なものであるため、この原則の実装は見た目よりも難しくなります。 このチュートリアルの例ほど単純ではありません。

これが次のポイントにつながります。

4. 凝集

SRPの原則に従って、クラスは1つの機能に準拠します。 彼らの方法とデータは、1つの明確な目的に関係しています。 これは、高い凝集度、、およびの堅牢性を意味し、エラーを削減します。

SRPの原則に基づいてソフトウェアを設計する場合、クラスの単一の責任を見つけるのに役立つため、凝集度が不可欠です。 この概念は、複数の責任を持つクラスを見つけるのにも役立ちます。

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;
}

...

ここでは、このクラスが行うことを明確に表しています。テキスト操作です。

しかし、まとまりについて考えず、このクラスの責任が何であるかを明確に定義していない場合、テキストの作成と更新は2つの異なる別個の仕事であると言えます。 この考えに基づいて、これらはWriteTextUpdateTextの2つの別個のクラスである必要があると結論付けることができます。

実際には、 2つのクラスが緊密に結合され、緩く凝集しているが得られます。これは、ほとんどの場合、一緒に使用する必要があります。 これらの3つの方法は、異なる操作を実行する可能性がありますが、基本的に1つの目的:テキスト操作に役立ちます。 重要なのは、考えすぎないことです。

メソッドの凝集度を高めるのに役立つツールの1つがLCOMです。 基本的に、 LCOMは、クラスコンポーネント間の接続とそれらの相互関係を測定します。

MartinHitzとBehzadMontazeriは、 LCOM4 を発表しました。これは、 Sonarqube がしばらくの間計測していましたが、その後廃止されました。

5. 結論

原則の名前は一目瞭然ですが、誤って実装するのがいかに簡単であるかがわかります。 プロジェクトを開発するときは、すべてのクラスの責任を区別し、結束に特に注意を払うようにしてください。

いつものように、コードはGitHubで利用できます。