단일 책임 원칙으로 고군분투


11

이 예제를 고려하십시오.

웹 사이트가 있습니다. 사용자가 게시물을 작성하고 (무엇이든 가능) 게시물을 설명하는 태그를 추가 할 수 있습니다. 코드에는 게시물과 태그를 나타내는 두 개의 클래스가 있습니다. 이 클래스 Post들을 호출 할 수 있습니다 Tag.

Post게시물 생성, 게시물 삭제, 게시물 업데이트 등 Tag을 처리합니다. 태그 생성, 태그 삭제, 태그 업데이트 등을 처리합니다.

누락 된 작업이 하나 있습니다. 태그를 게시물에 연결 누가이 작업을 수행해야하는지 고민하고 있습니다. 어느 클래스에서나 똑같이 잘 맞을 수 있습니다.

한편으로, Post클래스는 a Tag를 매개 변수로 사용하여 태그 목록에 저장 하는 함수를 가질 수 있습니다 . 한편, Tag클래스는 사용하는 함수 가질 수 Post매개 변수로와 링크 Tag받는 사람을 Post.

위는 내 문제의 예일뿐입니다. 나는 실제로 비슷한 여러 클래스로 이것을 실행하고 있습니다. 둘 다 똑같이 잘 맞을 수 있습니다. 실제로 두 클래스 모두에 기능을 배치하는 데 부족한이 문제를 해결하는 데 도움이되는 규칙 또는 디자인 스타일 나는 단지 하나를 선택하는 것보다 짧은 것이 있어야한다고 가정하고 있습니까?

어쩌면 두 수업 모두에 넣는 것이 정답입니까?

답변:


11

해적 코드와 마찬가지로 SRP는 규칙보다 지침이 아니며, 특히 잘 표현 된 것은 아닙니다. 대부분의 개발자는 Martin Fowler ( Refactoring )와 Robert Martin ( Clean Code ) 의 재정의를 받아 들여 클래스가 한 가지 책임이 아닌 변경해야 할 이유가 하나만 있어야한다고 제안합니다 .

좋은 견실 한 (말장난을 용서하는) 가이드 라인이지만, 그것을 무시하는 것처럼 끊는 것은 거의 위험합니다.

태그에 게시물을 추가 할 수 있고 그 반대의 경우에도 단일 책임 원칙을 위반하지 않았습니다. 객체의 구조가 변경되면 여전히 둘 다 변경해야 할 이유가 하나 있습니다. 하나의 구조를 변경해도 다른 구조가 추가되는 방식은 변경되지 않으므로 새 "책임"을 추가하지 않습니다.

최종 결정은 실제로 프론트 엔드에 필요한 기능에 의해 결정되어야합니다. 어떤 시점에서 게시물에 태그를 추가해야 할 수도 있으므로 다음과 같이하십시오.

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

나중에 태그에 게시물을 추가해야하는 경우 Tag 클래스에 addPost 메소드를 추가하고 Post 클래스에 referenceTag 메소드를 추가하십시오. 분명히 addPost에서 addTag를 호출하고 addTag에서 addPost를 호출하여 실수로 스택 오버플로가 발생하지 않도록 이름을 다르게 지정했습니다.


태그와 게시물의 관계는 다 대다라고 생각합니다.이 경우 태그가 여러 게시물에 대한 참조를 유지하는 것이 좋습니다. 단일 참조를 유지하면 어떻게 처리합니까?
Andres F.

@AndresF .: 나는 당신에게 동의하기 때문에 분명히 대답을 잘 쓰지 않았습니다. 나는 크게 편집했다. (이것이 의미를 바꾼 경우 이전 업보 터에게 사과드립니다.)
pdr

6

아니요, 둘 다 아닙니다! 한 곳에 있어야합니다.

귀하의 질문에 불분명 한 점은 귀하가 " Post게시물 작성, 게시물 삭제, 게시물 업데이트"등을 처리 한다는 사실입니다 Tag. 글쎄, 그건 옳지 않다. Post에 대한 업데이트 만 처리 할 수 ​​있습니다 Tag. 생성 및 삭제가 외부에 다른 사람의 작품 PostTag(그냥 호출하자 Store).

책임 Post은 "저자, 내용 및 최종 업데이트 날짜를 알고 있습니다". 좋은 책임 Tag은 "이름과 목적을 알고있다 (읽기 : 설명)"입니다. 좋은 책임 Store은 "모든 게시물과 모든 태그를 알고 있으며 추가, 제거 및 검색 할 수 있습니다"입니다.

이 세 참가자를 보면 가장 자연스럽게 Post-Tag 관계에 대한 지식이있는 사람은 누구입니까?

(나를 위해, 그것은 게시물이며, "태그를 알고있다"는 것이 당연해 보인다; 역 검색 (태그에 대한 모든 게시물)은 스토어의 일인 것 같다; 비록 내가 틀릴 수도있다)


각 게시물에 태그 목록이 있거나 각 태그에 포함 된 게시물 목록이있는 경우 "post x include tag y"라는 질문에 쉽게 대답 할 수 있습니다. 같은 클래스를 사용하는 것 외에 다른 클래스를 책임지지 않고 이러한 질문에 효율적으로 대답 할 수있는 방법 ConditionalWeakTable이 있습니까?
supercat

3

방정식에서 중요한 세부 사항이 누락되었습니다. 왜 태그에 포스트와 비자가 포함됩니까? 이 질문에 대한 대답은 주어진 각 세트에 대한 솔루션을 결정합니다.

일반적으로 비슷한 상황을 생각할 수 있습니다. 상자와 내용물. 상자에는 내용물이 있으므로 관계가 적절합니다. 내용물은 상자를 가질 수 있습니까? 물론, 상자가있는 상자. 상자는 내용물입니다. 그러나 IS-A는 모든 상자에 적합한 디자인은 아닙니다. 이 경우 데코레이터 패턴을 고려할 것입니다. 이 방법으로 상자는 필요에 따라 런타임에 내용으로 꾸며집니다.

태그도 게시물을 가질 수 있지만 나에게는 이것이 정적 관계가 아닙니다. 오히려 태그가있는 모든 게시물에 대한 보고서 일 수 있습니다. 이 경우에는 새로운 실체가있다.


2

이론적으로는 어떤 식 으로든 갈 수 있지만 실제로 구현에 도달하면 한 가지 방법이 다른 방법보다 거의 항상 더 적합합니다. 내 직감은 Post게시물에 대한 다른 것들이 동시에 변경 될 때 게시물을 만들거나 편집하는 동안 연결이 만들어 지므로 수업 에 더 적합하다는 것 입니다.

또한 여러 태그를 연결하고 하나의 데이터베이스 업데이트에서 수행하려는 경우 업데이트를 수행하기 전에 동일한 게시물과 관련된 모든 태그 목록을 작성해야합니다. 이 목록은 Post수업에 훨씬 적합합니다 .


1

개인적으로, 나는 그 기능을 그들 중 하나에 추가하지 않을 것입니다.

나를 위해, 모두 Post와는 Tag너무 데이터베이스 기능을 처리해서는 안, 데이터 개체입니다. 그들은 단순히 존재해야합니다. 데이터를 보유하고 응용 프로그램의 다른 부분에서 사용하기위한 것입니다.

대신 웹 페이지와 관련된 비즈니스 논리 및 데이터를 담당하는 다른 클래스가 있습니다. 페이지에 게시물이 표시되고 사용자가 태그를 추가 할 수있는 경우 클래스에는 Post객체가 있고 여기에 추가 Tags할 기능이 포함됩니다 Post. 페이지에 태그가 표시되고 사용자가 해당 태그에 게시물을 추가 할 수 있도록 허용하는 경우 Tag객체 가 포함 Posts되며 해당 기능을 추가 할 수 Tag있습니다.

그래도 나야 데이터 객체에서 데이터베이스 기능을 처리해야한다고 생각되면 pdr의 답변을 권합니다.


0

이 질문을 읽으면서 가장 먼저 떠오르는 것은 Many to Many 데이터베이스 관계였습니다. 게시물에는 많은 태그가있을 수 있습니다 ... 태그에는 많은 게시물이있을 수 있습니다 ... 두 클래스 모두이 관계를 어느 정도 관리 할 수있는 능력이 필요한 것으로 보입니다.

보기의 포스트 관점에서 ...
당신의 편집을하는 경우 또는 생성 포스트를 보조 활동이 관리된다 태그 관계를.

  1. 게시 할 기존 태그 추가
  2. 게시물에서 태그 수정

완전히 새로운 TAG를 생성하는 IMO는 여기에 속하지 않습니다.

태그 관점에서 ... 태그 를 게시물에 할당하지 않고도 태그
만들 수 있습니다 . 게시물과 상호 작용하는 유일한 활동은 태그 삭제 기능입니다. 그러나이 기능은 독립적 인 독립 기능이어야합니다.

다 대 다 관계를 해결하는 데이터베이스 연결 테이블이있는 경우에만 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.