単一責任の原則との闘い


11

この例を考えてみましょう:

ウェブサイトを持っています。これにより、ユーザーは投稿(何でも可能)を作成し、投稿を説明するタグを追加できます。コードには、投稿とタグを表す2つのクラスがあります。これらのクラスをPostおよびと呼びましょうTag

Post投稿の作成、投稿の削除、投稿の更新など Tagを処理します。タグの作成、タグの削除、タグの更新などを処理します。

欠落している操作が1つあります。タグと投稿のリンク。私は誰がこの手術をすべきかと格闘しています。どちらのクラスにも等しく適合する可能性があります。

一方では、PostクラスにTagaをパラメーターとして受け取り、タグのリストに格納する関数を含めることができます。一方、Tagクラスが取る関数可能性がありPost、パラメータとして及びリンクTagにしますPost

上記は私の問題の一例です。私は実際には、すべて類似した複数のクラスでこれに直面しています。それは両方に等しく適合する可能性があります。実際に両方のクラスに機能を配置するのではなく、この問題を解決するのに役立つ規則やデザインスタイルが存在します。私は1つを選ぶだけでは足りないものがあるはずだと思いますか?

たぶん両方のクラスに入れるのが正しい答えでしょうか?

回答:


11

海賊コードと同様に、SRPはルールというよりもガイドラインであり、特に明確な言葉ではありません。ほとんどの開発者は、Martin Fowler(リファクタリング)とRobert Martin(Clean Code)の再定義を受け入れており、クラスには(1つの責任ではなく)変更する理由が1つだけあるべきだと示唆してます。

それは良い、堅実な(しゃれを許さない)ガイドラインですが、それを無視するのと同じくらい、それに固執するのはほとんど危険です。

あなたがタグにポストを追加することができる場合、その逆、あなたはシングル責任の原則を破っていません。両方とも、そのオブジェクトの構造が変更された場合、変更する理由は1つしかありません。どちらか一方の構造を変更しても、もう一方に追加される方法は変更されないため、新しい「責任」を追加することはありません。

最終的な決定は、フロントエンドで必要な機能によって実際に決定される必要があります。ある時点でタグを投稿に追加する必要がある可能性が高いので、次のようにします。

// C-style-language pseudo-code
class Post {
    string _title;
    string _content;
    Date _date;
    List<Tag> _tags;

    Post(string title, string content) {
        _title = title;
        _content = content;
        _date = Now;
        _tags = new List<Tag>();
    }

    Tag[] getTags() {
        return _tags.toArray();
    }

    void addTag(Tag tag) {
        if (_tags.contains(tag)) {
            throw "Cannot add tag twice";
        }

        _tags.Add(tag);
        tag.referencePost(this);
    }

    // more stuff here, obviously
}

class Tag {
    string _name;
    List<Post> _posts;

    Tag(string name) {
        _name = name;
    }

    Post[] getPosts() {
        return _posts.toArray();
    }

    void referencePost(Post post) {
        if (!post.getTags().contains(this) || _posts.contains(post)) {
            throw "Only reference a post by calling Post.addTag()";
        }

        _posts.Add(post);
    }

    // more stuff here too
}

後で、タグにも投稿を追加する必要がある場合は、addPostメソッドをTagクラスに追加し、referenceTagメソッドをPostクラスに追加するだけです。明らかに、addPostからaddTagを、addTagからaddPostを呼び出して誤ってスタックオーバーフローを引き起こさないように、それらに異なる名前を付けました。


TagとPostの関係は多対多だと思います。その場合、Tagが複数のPostへの参照を保持するのが理にかなっています。単一の参照を保持する場合、これをどのように処理しますか?
アンドレスF.

@AndresF .:私はあなたに同意するので、私は明らかに私の答えをあまり上手く書けなかった。私は大幅に編集しました。(これがあなたがそれを見たように意味を変えるならば、前の支持者に謝罪します。)
pdr

6

いいえ、両方ではありません!1つの場所にある必要があります。

あなたの質問で私が不愉快だと思うのは、あなたが「Post投稿の作成、投稿の削除、投稿の更新を処理します」と言っているという事実ですTag。まあ、それは正しくありません。Postの更新のみを処理できTagます。作成と削除は、外部​​のPostand の作業ですTag(呼び出してみましょうStore)。

責任Postは「作成者、内容、最終更新日を知っている」ことです。責任Tagは「その名前と目的を知っている(読む:説明)」です。責任Storeは「すべての投稿とすべてのタグを認識し、それらを追加、削除、検索できる」ことです。

これらの3人の参加者を見ると、タグ付け後の関係の知識が必要な人は誰ですか?

(私にとっては、それが「タグ」を知っているのは当然のことです。逆検索(タグのすべての投稿)はストアの仕事のようです。私は間違っているかもしれませんが)


各投稿にタグのリストがある場合、および/または各タグにそれを含む投稿のリストがある場合、「投稿xはタグyを含む」という質問に簡単に答えることができます。クラスのいずれかが責任を負うことなく、そのような質問に効率的に答える方法はありますConditionalWeakTableか?(フレームワークが存在する幸運だと仮定して)
supercat

3

方程式には重要な詳細が欠落しています。タグに郵便とビザが含まれているのはなぜですか?この質問に対する答えにより、各セットのソリューションが決まります。

一般的に私は似たような状況を考えることができます。箱と中身。ボックスには内容が含まれているため、has-a関係が適切です。内容物に箱を入れることはできますか?確かに、箱と箱。箱は中身です。ただし、IS-Aはすべてのボックスに適した設計ではありません。そのような場合、デコレータパターンを検討します。このようにして、必要に応じて、実行時にボックスがコンテンツで装飾されます。

タグにも投稿を含めることができますが、私にとってこれは静的な関係ではありません。むしろ、それはタグを言ったすべての投稿のレポートかもしれません。この場合、has-aではなく、新しいエンティティです。


2

理論的にはそのようなことはどちらの方法でも可能ですが、実際には、実装に着手するとき、ほとんどの場合、他の方法よりも適しています。Post投稿に関する他の事柄が同時に変化している場合、投稿の作成または編集中に関連付けが作成されるため、クラスにうまく収まると思います。

また、複数のタグを関​​連付けて、1回のデータベース更新でそれを行う場合は、更新を行う前に同じ投稿に関連付けられているすべてのタグのリストを作成する必要があります。このリストは、Postクラスにはるかに適しています。


1

個人的には、その機能をどちらにも追加しません。

私には、両方PostTagデータオブジェクトなので、データベース機能を扱うべきではありません。それらは単に存在する必要があります。データを保持し、アプリケーションの他の部分で使用するためのものです。

代わりに、Webページに関連するビジネスロジックとデータを担当する別のクラスがあります。ページに投稿が表示され、ユーザーがタグを追加できるようにしているPost場合、クラスにはオブジェクトがあり、それに追加Tagsする機能が含まれますPost。ページにタグが表示されており、ユーザーがそれらのタグに投稿を追加できるようにしている場合、Tagオブジェクトが含まれ、それに追加Postsする機能がありますTag

それは私だけです。データオブジェクトのデータベース機能を処理する必要があると思われる場合は、pdrの答えをお勧めします


0

この質問を読んだときに最初に思いついたのは、多対多のデータベース関係です。投稿には多くのタグを含めることができます...タグには多くのタグを含めることができます....両方のクラスには、ある程度この関係を管理する機能が必要であるように思われます。

ビューのポストの観点から...
あなたの編集や作成する場合はポストを第二の活動は、管理となり、タグの関係を。

  1. 既存のタグを投稿に追加
  2. 投稿からタグを削除

IMO、完全に新しいTAGの作成はここに属しません。

タグの観点から...投稿に割り当てることなくタグ
作成できます。投稿とのやり取りに関連する唯一のアクティビティは、タグ削除機能です。ただし、この関数は独立したスタンドアロン関数でなければなりません。

これは、多対多の関係を解決するデータベースリンクテーブルがある場合にのみ機能します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.