이 클래스 디자인이 단일 책임 원칙을 위반합니까?


63

오늘 나는 누군가와 논쟁했다.

빈혈 도메인 모델이 아닌 리치 도메인 모델의 이점을 설명했습니다. 그리고 나는 간단한 수업으로 내 요점을 시연했습니다.

public class Employee
{
    public Employee(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastname;
    }

    public string FirstName { get private set; }
    public string LastName { get; private set;}
    public int CountPaidDaysOffGranted { get; private set;}

    public void AddPaidDaysOffGranted(int numberOfdays)
    {
        // Do stuff
    }
}

그의 빈혈 모델 접근 방식을 변호하면서 그의 주장 중 하나는 "저는 SOLID 의 신자입니다 . 동일한 클래스에서 데이터를 표현하고 논리를 수행 할 때 단일 책임 원칙 (SRP)을 위반하고 있습니다."

이 주장에 따라 하나의 속성과 하나의 메소드를 가진 클래스가 SRP를 위반 하므로이 주장이 정말 놀랍습니다. 따라서 OOP는 일반적으로 SOLID가 아니며 함수형 프로그래밍 은 천국에 이르는 유일한 방법입니다.

나는 그의 많은 주장에 대답하지 않기로 결정했지만, 커뮤니티가이 질문에 대해 어떻게 생각하는지 궁금합니다.

내가 대답했다면 위에서 언급 한 역설을 지적하면서 시작한 다음 SRP가 고려하고 싶은 세분성 수준에 크게 의존하고 있으며, 너무 많이 가져 가면 클래스가 하나 이상 포함되어 있음을 나타냅니다. 속성 또는 하나의 방법으로 위반합니다.

뭐라고 했어?

업데이트 : 예제는 guntbert에 의해 관대하게 업데이트되어 방법을보다 현실적으로 만들고 기본 토론에 집중할 수 있도록 도와줍니다.


19
이 클래스는 SRP 위반 는 논리와 데이터를 혼합하기 때문에하지,하지만 낮은 응집력이 있기 때문에 - 잠재적 하나님 객체
모기

1
직원에게 휴일을 추가하는 목적은 무엇입니까? 공휴일이있는 달력 수업이나 무언가가있을 것입니다. 나는 당신의 친구가 옳다고 생각합니다.
James Black

9
"나는 X의 신자"라고 말하는 사람의 말을 듣지 마십시오.
Stig Hemmer

29
이것이 SRP를 위반하는지 여부는 그리 좋은 모델링인지 여부입니다. 내가 직원이라고 가정하십시오. 스키를 타는데 긴 주말이 걸리면 관리자에게 괜찮은지 물어볼 때 관리자는 휴일을 추가 하지 않습니다 . 여기의 코드는 모델링하려는 실제와 일치하지 않으므로 의심됩니다.
Eric Lippert

2
기능 프로그래밍이 천국으로가는 유일한 길 +1.
erip

답변:


68

단일 책임은 시스템에서 논리적 작업의 추상화로 이해해야합니다. 클래스는 하나의 특정 작업을 수행하는 데 필요한 단일 책임을 가져야합니다. 이것은 실제로 책임이 무엇인지에 따라 잘 설계된 수업에 많은 것을 가져올 수 있습니다. 예를 들어 스크립트 엔진을 실행하는 클래스에는 스크립트 처리와 관련된 많은 메서드와 데이터가있을 수 있습니다.

동료가 잘못된 것에 집중하고 있습니다. 문제는 "이 클래스의 멤버는 무엇입니까?"가 아닙니다. 그러나 "이 클래스는 프로그램 내에서 어떤 유용한 작업을 수행합니까?" 일단 이해하면 도메인 모델은 잘 보입니다.


객체 지향 프로그래밍이 작성되었고 실제 객체 및 클래스 (액션 + 속성)를 모델링하려는 경우 , 여러 책임 (액션)을 갖는 코드 클래스 를 작성하지 않는 이유는 무엇입니까? 실제 객체는 여러 가지 책임을 가질 수 있습니다. 예를 들어 기자 는 신문에 사설을 쓰고 TV 쇼에서 정치인을 인터뷰합니다. 실제 물체에 대한 두 가지 책임! 학급 기자를 쓰려고하면 어떻게합니까?
user1451111

41

단일 책임 원칙은 코드 조각 (일반적으로 우리가 클래스에 대해 이야기하는 OOP에서)이 하나의 기능을 담당하는지 여부에만 관심이 있습니다 . 함수와 데이터를 혼합 할 수 없다고 말하는 친구가 그 아이디어를 실제로 이해하지 못했다고 생각합니다. Employee그의 직장에 대한 정보, 그의 차가 얼마나 빠르는지, 그리고 그의 개가 먹는 음식의 종류에 관한 정보도 포함 한다면 문제가 생길 것입니다.

이 클래스는을 다루기 때문에 EmployeeSRP를 맹목적으로 위반하지 않는다고 말하는 것이 공정하다고 생각하지만 사람들은 항상 자신의 의견을 가지고 있습니다.

개선 할 수있는 한 가지 장소는 직원 정보 (이름, 전화 번호, 이메일 등)를 휴가와 분리하는 것입니다. 내 생각에 이것은 메서드와 데이터가 혼합 될 수 없다는 것을 의미하지는 않으며 휴가 기능이 별도의 장소에있을 수 있음을 의미합니다.


20

내 생각 에이 클래스는 S Employee와 A를 계속 나타내는 경우 SRP를 위반 할 수 있습니다 EmployeeHolidays.

그대로, 그리고 피어 리뷰를 위해 나에게 온다면 아마 그것을 통과시킬 것입니다. 더 많은 직원 별 속성과 방법이 추가되고 더 많은 휴일 별 속성이 추가되면 SRP와 ISP를 모두 언급하여 분할을 권할 것입니다.


동의한다. 코드가 여기에 제공된 것처럼 간단하다면 아마 슬라이드하게 될 것입니다. 그러나 내 마음에, 나는 직원이 자신의 휴일을 처리하는 책임을지지 않아야합니다. 책임이있는 위치에는 큰 문제가 아닌 것처럼 보일 수 있지만 다음과 같이보십시오. 코드베이스를 처음 사용하고 공휴일 관련 기능을 수행 해야하는 경우 먼저 어디를 보시겠습니까? 휴일 논리의 경우, 나는 개인적으로 직원 엔티티를 보지 않을 것입니다.
Niklas H

1
@NiklasH 합의. 개인적으로 나는 무작위로 보지 않고 "홀리데이"라는 단어를 찾기 위해 스튜디오를 검색하고 어떤 클래스가 등장했는지 수업을 추측하고 추측합니다. :)
NikolaiDante

4
참된. 그러나이 새로운 시스템에서 "휴일"이라고하지 않고 "휴가"또는 "자유 시간"인 경우 어떻게해야합니까? 그러나 귀하는 일반적으로 검색 만하거나 동료에게 요청할 수있는 능력에 동의합니다. 내 의견은 주로 OP가 책임을 정신적으로 모델링하고 논리의 가장 분명한 위치는 다음과 같은 위치에있게하는 것이 었습니다.
Niklas H

나는 당신의 첫 진술에 동의합니다. 그러나 동료 검토에 따르면 SRP를 위반하는 것이 미끄러운 경사이기 때문에 많은 깨진 창문 중 첫 번째가 될 수 있다고 생각하지 않습니다. 건배.
Jim Speaker

20

SRP가 논리적 기능에 대한 추상 개념이라는 지적은 이미 훌륭하지만 추가 할 가치가 있다고 생각되는 미묘한 점이 있습니다.

SOLID, SRP 및 OCP의 처음 두 글자는 요구 사항 변경에 따른 코드 변경 방법에 관한 것입니다. 내가 가장 좋아하는 SRP 정의는 "모듈 / 클래스 / 함수에는 변경해야 할 단 하나의 이유가 있어야합니다."입니다. 코드가 변경 될 가능성이 높은 이유에 대해 논쟁하는 것은 코드가 SOLID인지 아닌지를 논하는 것보다 훨씬 생산적입니다.

직원 클래스는 몇 가지 이유를 바꿔야합니까? 나는 당신이 그것을 사용하고있는 상황을 모르고 미래를 볼 수 없기 때문에 모르겠습니다. 내가 할 수있는 것은 과거에 본 것을 기반으로 가능한 변화를 브레인 스토밍하는 것입니다. "합리적으로 가능성이 높음"과 "정확한 이유로 내 코드가 이미 변경되었습니다"사이에 둘 이상의 점수가 있으면 해당 유형의 변경에 대해 SRP를 위반 한 것입니다. 하나는 다음과 같습니다. 둘 이상의 이름을 가진 사람이 회사에 합류합니다 (또는 건축가 가이 훌륭한 W3C 기사를 읽습니다 ). 여기 또 다른 예가 있습니다. 회사는 휴일을 할당하는 방법을 변경합니다.

AddHolidays 메소드를 제거하더라도 이러한 이유는 동일하게 유효합니다. 많은 빈혈 도메인 모델이 SRP를 위반합니다. 그중 다수는 코드에서 데이터베이스 테이블 일 뿐이며 데이터베이스 테이블에서 20 가지 이상의 변경 이유가있는 것이 일반적입니다.

시스템에서 직원 급여를 추적해야하는 경우 직원 클래스가 변경됩니까? 구애? 긴급 연락처 정보? 그 중 두 가지에 "예"(그리고 일어날 가능성이 있음)라고 말하면, 아직 코드가 없더라도 클래스가 SRP를 위반했을 것입니다! SOLID는 코드와 관련하여 프로세스 및 아키텍처에 관한 것입니다.


9

클래스가 데이터를 나타내는 것은 클래스의 책임이 아니며 개인 구현 세부 사항입니다.

수업은 직원을 대표 할 책임이 있습니다. 이러한 맥락에서 이는 직원을 다루는 데 필요한 기능을 제공하는 공개 API를 제공함을 의미합니다 (AddHolidays가 좋은 예인지 여부는 논쟁의 여지가 없습니다).

구현은 내부적입니다. 개인 변수와 논리가 필요합니다. 그렇다고 클래스에 여러 책임이 있다는 의미는 아닙니다.


재미있는 사고 방식, 공유해 주셔서 감사합니다
tobiak777

니스-OOP의 목표를 표현하는 좋은 방법입니다.
user949300

5

어떤 식 으로든 논리와 데이터를 혼합하는 것이 항상 잘못되었다는 생각은 너무 어리석어 서 토론조차 할 가치가 없습니다. 그러나이 예에서는 실제로 단일 책임에 대한 명백한 위반이 있지만 속성 DaysOfHolidays과 기능 이 없기 때문이 아닙니다 AddHolidays(int).

직원의 신원이 휴일 관리와 섞여 있기 때문입니다. 직원의 신원은 휴가, 연봉, 초과 근무를 추적하고, 어떤 팀에 속한 사람을 나타내는 지, 성과 보고서에 연결하는 등의 복잡한 작업입니다. 직원은 이름, 성 또는 둘 다를 변경할 수도 있습니다. 종업원. 직원은 ASCII 및 유니 코드 철자와 같이 이름의 철자가 여러 개인 경우도 있습니다. 사람들은 이름과 성을 0에서 n까지 가질 수 있습니다. 관할 지역마다 이름이 다를 수 있습니다. 직원의 신원을 추적하는 것은 휴일 또는 휴가 관리를 두 번째 책임이라고 부르지 않고 맨 위에 추가 할 수없는 책임으로 충분합니다.


"직원의 신원을 추적하는 것은 휴일 또는 휴가 관리를 두 번째 책임이라고 부르지 않고 맨 위에 추가 할 수없는 책임으로 충분합니다." + 직원은 여러 이름 등을 가질 수 있습니다. 모델의 요점은 현재 문제에 대한 실제 세계의 관련 사실에 초점을 맞추는 것입니다. 이 모델이 최적의 요구 사항이 있습니다. 이러한 요구 사항에서 직원은 휴일을 수정할 수있는 정도로만 흥미를 느끼며 실제 세부 사항의 다른 측면을 관리하는 데 너무 관심이 없습니다.
tobiak777

@reddy "직원은 휴일을 수정할 수있는 정도로만 흥미 롭습니다."–이를 정확하게 식별해야합니다. 직원이있는 즉시 결혼이나 이혼으로 인해 언제든지 성을 변경할 수 있습니다. 또한 이름과 성별을 변경할 수 있습니다. 이름이 다른 직원의 이름과 일치하도록 성이 변경되면 직원을 해고 하시겠습니까? 지금이 기능을 모두 추가하지는 않습니다. 대신 필요할 때 추가 할 수 있습니다. 구현 된 금액에 관계없이 식별 책임은 동일하게 유지됩니다.
Peter

3

"저는 SOLID를 믿고 있습니다. 동일한 클래스에서 데이터를 표현하고 논리를 수행 할 때 단일 책임 원칙 (SRP)을 위반하고 있습니다."

다른 사람들처럼, 나는 이것에 동의하지 않습니다.

수업에서 둘 이상의 논리 를 수행하는 경우 SRP가 위반되었다고 말할 수 있습니다 . 단일 논리를 달성하기 위해 클래스 내에 얼마나 많은 데이터를 저장해야하는지는 중요하지 않습니다.


아니! SRP는 여러 논리 조각, 여러 데이터 조각 또는이 둘의 조합으로 위반되지 않습니다. 유일한 요구 사항은 객체가 목적에 충실해야한다는 것입니다. 그 목적은 많은 작업을 수반 할 수 있습니다.
Martin Maat

@MartinMaat : 많은 작업입니다. 예. 결과적으로 하나의 논리. 나는 우리가 있지만 다른 용어와 함께 같은 일을 말하고 있다고 생각 (그리고 나는 종종이 물건을 공부하지 않는 한 당신이 올바른 사람을 있다고 가정 행복하다)
밝기 경주 궤도에

2

요즘 단일 책임이나 변화의 단일 이유를 구성하는 것이 무엇인지에 대해 토론하는 것이 유용하지 않습니다. 그 자리에 최소 슬픔 원칙을 제안합니다.

최소 슬픔 원칙 : 코드는 변경이 필요할 확률을 최소화하거나 변경의 용이성을 최대화해야합니다.

어떻게한다는거야? 로켓 과학자가 왜 이것이 유지비를 줄이는 데 도움이 될 수 있는지 알아 내지 말아야하며 끝없이 논쟁의 여지가되지 않기를 바랍니다. 그러나 일반적으로 SOLID와 마찬가지로 맹목적으로 적용되는 것은 아닙니다. 균형을 유지하면서 고려해야 할 사항입니다.

변경이 필요할 확률은 다음과 같습니다.

  1. 좋은 테스트 (신뢰성 향상).
  2. 특정 작업을 수행하는 데 필요한 최소한의 코드 만 포함합니다 (심각한 커플 링 감소 포함).
  3. 코드의 기능에 따라 코드를 잘못 작성하기 만하면됩니다 (잘못된 규칙 만들기 참조).

변경의 어려움에 관해서는 efferent coupling으로 올라갑니다. 테스트는 다른 커플 링을 소개하지만 신뢰성을 향상시킵니다. 잘 했어요, 그것은 일반적으로 해보다 더 잘하고 최소 허용 원칙에 의해 완전히 수용되고 촉진됩니다.

Badass Principle 만들기 : 많은 곳에서 사용되는 클래스는 훌륭해야합니다. 품질과 관련이있는 경우 신뢰할 수 있고 효율적이어야합니다.

그리고 불량배 만들기 원칙은 최소 슬픔 원칙과 연결되어 있습니다.

위에서 언급 한 역설을 지적하면서 시작한 다음 SRP가 고려하려는 세분성 수준에 크게 의존하고 있으며,이를 충분히 취하면 둘 이상의 속성이나 메서드가 포함 된 클래스가 위반됨을 나타냅니다. 그것.

SRP 관점에서 볼 때 거의 아무것도하지 않는 클래스는 변경해야 할 이유가 하나만 있습니다 (때로는 0).

class Float
{
public:
    explicit Float(float val);
    float get() const;
    void set(float new_val);
};

실제로 변경할 이유가 없습니다! SRP보다 낫습니다. ZRP입니다!

내가 그것이 나쁜 행동 원칙을 명백히 위반하고 있다고 제안하는 것을 제외하고는. 또한 절대적으로 가치가 없습니다. 그렇게 적은 일이 엉망이되기를 희망 할 수는 없습니다. 정보가 너무 적습니다 (TLI). 그리고 당연히 TLI라는 것을 가지고 있다면 캡슐화 한 정보조차도 실제로 의미있는 일을 할 수 없으므로 다른 누군가가 실제로 의미 있고 나쁜 일을하기를 바라면서 외부 세계로 유출해야합니다. 그리고 누설은 데이터를 모으기위한 것이지만, 그 임계 값은 "데이터"와 "개체"의 차이입니다.

물론 TMI 인 것도 나쁘다. 전체 소프트웨어를 한 클래스에 넣을 수도 있습니다. 하나의 run방법 만 가질 수도 있습니다 . 그리고 누군가는 이제 변화해야 할 매우 광범위한 이유가 있다고 주장 할 수도 있습니다. "이 클래스는 소프트웨어가 개선되어야하는 경우에만 변경하면됩니다." 나는 바보 스럽지만 물론 우리는 그와 관련된 모든 유지 관리 문제를 상상할 수 있습니다.

따라서 디자인 한 개체의 세분성 또는 거칠기에 대한 균형 조정 작업이 있습니다. 나는 종종 외부에 유출해야하는 정보의 양과 그것이 수행 할 수있는 의미있는 기능의 양으로 판단합니다. 나는 종종 Bad Bad Principle이 최소 슬픔 원리와 결합하면서 균형을 찾는 데 도움이된다는 것을 알게되었습니다.


1

반대로, 나를 위해 Anemic 도메인 모델은 일부 OOP 주요 개념 (속성과 속성을 연결 함)을 깨뜨 렸지만 아키텍처 선택에 따라 불가피 할 수 있습니다. 빈혈 영역은 생각하기 쉽고 유기성이 떨어지며 순차적입니다.

많은 시스템이 여러 계층이 동일한 데이터 (서비스 계층, 웹 계층, 클라이언트 계층, 에이전트 등)로 재생해야 할 때이 작업을 수행하는 경향이 있습니다.

한 곳에서 데이터 구조를 정의하고 다른 서비스 클래스에서 동작을 정의하는 것이 더 쉽습니다. 동일한 계층이 여러 계층에 사용 된 경우이 계층이 커질 수 있으며 필요한 동작을 정의 할 책임이있는 계층과 누가 메소드를 호출 할 수 있는지에 대한 질문을합니다.

예를 들어, 모든 직원에 대한 통계를 계산하는 에이전트 프로세스가 유급 일에 대한 계산을 호출 할 수있는 것은 좋은 방법이 아닙니다. 그리고 직원 목록 GUI는이 통계 에이전트 (및 함께 제공되는 기술 데이터)에 사용 된 새로운 집계 ID 계산이 전혀 필요하지 않습니다. 이 방법으로 메소드를 분리하면 일반적으로 데이터 구조 만있는 클래스로 끝납니다.

객체 개념 / 책임에 대해 걱정하지 않고 "객체"데이터 또는 일부 데이터 또는 다른 형식 (json)으로 너무 쉽게 직렬화 / 역 직렬화 할 수 있습니다. 그것은 단지 데이터 전달입니다. 두 클래스 (Employee, EmployeeVO, EmployeeStatistic…)간에 항상 데이터 매핑을 수행 할 수 있지만 여기서 Employee는 실제로 무엇을 의미합니까?

예, 도메인 클래스의 데이터와 서비스 클래스의 데이터 처리를 완전히 분리하지만 여기에 필요합니다. 이러한 시스템은 동시에 비즈니스 가치를 가져 오는 기능적인 시스템이며, 적절한 책임 범위를 유지하면서 필요한 곳 ​​어디에서나 데이터를 전파 할 수있는 기술적 인 시스템입니다 (분산 개체도이 문제를 해결하지 못합니다).

동작 범위를 분리 할 필요가없는 경우 객체를 보는 방법에 따라 서비스 클래스 또는 도메인 클래스에 메소드를 자유롭게 배치 할 수 있습니다. 나는 객체를 "실제"개념으로 보는 경향이있다. 이것은 자연스럽게 SRP를 유지하는 데 도움이된다. 예를 들어, 직원의 보스가 PayDayAccount에 부여 된 월급을 추가하는 것보다 더 현실적입니다. 직원이 회사, 직원에 의해 고용되어 병에 걸리거나 조언을 요청할 수 있으며, Payday 계정이 있습니다 (보스가 직접 또는 PayDayAccount 레지스트리에서이를 검색 할 수 있습니다 ...) 그러나 통합 된 바로 가기를 만들 수 있습니다 간단한 소프트웨어에 대해 너무 많은 복잡성을 원하지 않는 경우 단순성을 위해 여기에 있습니다.


입력 Vince에 감사드립니다. 요점은 리치 도메인이있는 경우 서비스 계층이 필요하지 않다는 것입니다. 행동을 담당하는 클래스는 단 하나 뿐이며 도메인 엔티티입니다. 다른 계층 (웹 계층, UI 등)은 일반적으로 DTO 및 ViewModel을 처리하며 이는 정상입니다. 도메인 모델은 도메인을 모델링하고 UI 작업을하지 않거나 인터넷을 통해 메시지를 보내는 것입니다. 귀하의 메시지는 이러한 일반적인 오해를 반영합니다. 이는 사람들이 단순히 OOP를 설계에 맞추는 방법을 모른다는 사실에서 비롯됩니다. 그리고 나는 이것이 매우 슬프다 고 생각합니다.
tobiak777

리치 도메인 OOP에 대한 오해가 없다고 생각하고, 그런 방식으로 많은 소프트웨어를 설계했으며 실제로 유지 관리 / 진화에 좋습니다. 그러나 이것이 은총 알이 아니라고 유감입니다.
Vince

나는 그것을 말하는 것이 아닙니다. 컴파일러를 작성하는 것은 아마 아닐 수도 있지만 대부분의 비즈니스 / SaaS 응용 프로그램에서는 제안한 것보다 예술 / 과학이 훨씬 적다고 생각합니다. 도메인 모델의 필요성은 수학적으로 입증 될 수 있습니다. 귀하의 사례를 통해 OOP의 한계가 아니라 논쟁의 여지가있는 디자인을 생각할 수 있습니다.
tobiak777

0

동일한 클래스에서 데이터를 표현하고 논리를 수행 할 때 단일 책임 원칙 (SRP)을 위반하고 있습니다.

그것은 나에게 매우 합리적으로 들린다. 조치가 노출되면 모델에 공용 특성이 없을 수 있습니다. 기본적으로 Command-Query Separation 아이디어입니다. Command는 확실히 개인 상태를 갖습니다.


0

단일 책임 원칙 은 자연의 규칙이 아니라 미적 기준이기 때문에 위반할 수 없습니다 . 과학적으로 들리는 이름과 대문자로 오해하지 마십시오.


1
이것은 실제 답변은 아니지만 질문에 대한 의견으로 좋을 것입니다.
Jay Elston
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.