단일 책임 원칙-과도하게 사용합니까?


13

참조를 위해-http: //en.wikipedia.org/wiki/Single_responsibility_principle

하나의 응용 프로그램 모듈에서 원장 항목을 생성하는 테스트 시나리오가 있습니다. 수행 할 수있는 세 가지 기본 작업이 있습니다-

  • 기존 원장 항목을 테이블 형식으로 봅니다.
  • 작성 단추를 사용하여 새 원장 항목을 작성하십시오.
  • 표에서 원장 항목 (첫 번째 포인터 참조)을 클릭하고 다음 페이지에서 세부 사항을보십시오. 이 페이지에서 원장 항목을 무효화 할 수 있습니다.

(각 페이지에는 몇 가지 작업 / 확인이 있지만 간략하게하기 위해이를 제한 할 것입니다)

그래서 나는 세 가지 다른 클래스를 만들기로 결정했습니다.

  • 원장
  • CreateNewLedgerEntryPage
  • ViewLedgerEntryPage

이 클래스는 해당 페이지에서 수행 할 수있는 서비스를 제공하며 Selenium 테스트는이 클래스를 사용하여 특정 어설 션을 만들 수있는 상태로 응용 프로그램을 만듭니다.

내가 동료와 함께 그것을 검토하게되었을 때, 그는 압도되어 모든 사람을 위해 하나의 수업을 만들도록 요청했다. 아직 디자인이 훨씬 깨끗하다고 ​​생각하지만 Single Responsibility 원칙을 과도하게 사용하고 있는지 의심됩니다.


2
IMHO는 두 가지를 모른 채 일반적인 질문에 대답하려고 할 필요가 없습니다. 그리고 가까운 미래에 그들이 얼마나 더 크고 더 복잡해질 것으로 예상됩니까?
Péter Török

2
각각 10 개의 메소드는 30 개의 메소드를 사용하여 하나의 클래스에 코드를 넣지 말아야한다는 명확한 표시입니다.
Doc Brown

6
이것은 경계 영역입니다. 100 LOC 클래스와 10 메소드는 너무 작지 않으며 300 LOC 중 하나는 너무 크지 않습니다 . 그러나 단일 클래스의 30 가지 방법은 나에게 너무 많이 들립니다. 전반적으로, 나는 단일 과체중 클래스를 갖는 것보다 많은 작은 클래스를 갖는 것이 덜 위험하다는 점에서 @Doc에 동의하는 경향이 있습니다. 특히 수업 시간이 지남에 따라 자연적으로 체중이 증가하는 경향을 고려하면 ...
Péter Török

1
@ PéterTörök-OP가 기대하는 것처럼 기능을 복제하는 대신 코드를 재사용하는 직관적으로 사용할 수있는 하나의 클래스로 코드를 리팩터링 할 수 없다면 동의합니다.
SoylentGray

1
아마도 MVC를 적용하면 세 가지 뷰, 하나의 모델 (Ledger) 및 세 개의 컨트롤러가이 문제를 해결하게 될 것입니다.
케빈 클라인

답변:


19

@YannisRizos를 인용 여기 :

이것은 본능적 인 접근 방식이며 아마도 변경 가능성이 더 큰 것이 원장 관리의 비즈니스 논리이기 때문에 아마도 정확할 것입니다. 그렇게되면 한 클래스를 세 개보다 유지하는 것이 훨씬 쉽습니다.

나는 적어도 약 30 년의 프로그래밍 경험 후에 동의하지 않습니다. 작은 클래스 IMHO는 이와 같은 경우에 생각할 수있는 거의 모든 경우 에 유지하는 것이 좋습니다 . 하나의 클래스에 더 많은 함수를 넣을수록 구조는 줄어들고 코드의 품질은 매일 저하됩니다. Tarun을 올바르게 이해하면이 3 가지 작업 각각

LedgerLandingPage

CreateNewLedgerEntryPage

ViewLedgerEntryPage

유스 케이스입니다. 이러한 각 클래스는 관련 태스크를 수행하기위한 둘 이상의 함수로 구성되며 각 클래스는 별도로 개발 및 테스트 할 수 있습니다. 따라서 이들 각각에 대한 클래스를 작성하면 함께 속하는 것을 그룹화하여 무언가 변경해야 할 경우 변경 될 코드 부분을 훨씬 쉽게 식별 할 수 있습니다.

이 3 개의 클래스간에 공통 기능이있는 경우 공통 기본 클래스, Ledger클래스 자체 (적어도 CRUD 작업에는 하나가 있다고 가정) 또는 별도의 도우미 클래스에 속합니다 .

더 작은 클래스를 만들기 위해 더 많은 인수가 필요하다면 Robert Martin의 저서 "Clean Code"를 살펴 보는 것이 좋습니다 .


그것이 내가 한 클래스에서 모든 것을 푸시하는 대신 세 개의 diff 클래스를 생각 해낸 이유입니다. 이들 중 공통 기능이 없기 때문에 공통 클래스가 없습니다. 링크 주셔서 감사합니다.
Tarun

2
"작은 수업은 내가 생각할 수있는 거의 모든 경우에 유지하는 것이 좋습니다." Clean Code조차도 그렇게 멀지 않다고 생각합니다. 예를 들어 MVC 패턴에서 페이지 당 하나의 컨트롤러가 있어야한다고 주장하겠습니까 (예 : 사용 사례). 리팩토링에서 Fowler에는 두 가지 코드 냄새가 있습니다. 하나는 클래스를 변경해야하는 많은 이유 (SRP)를 나타내는 것과 다른 하나는 하나의 변경을 위해 많은 클래스를 편집해야하는 것을 말합니다.
pdr

@ pdr, OP는이 클래스들 사이에 공통적 인 기능이 없다고 말하므로 단일 변경을 위해 하나 이상을 터치 ​​해야하는 상황을 상상하기가 어렵습니다.
Péter Török

@ pdr : 한 번의 변경으로 편집 할 클래스가 너무 많으면 대부분 공통 기능을 별도의 장소로 리팩토링하지 않은 징후입니다. 그러나 위의 예에서 이것은 아마도 하나의 클래스가 아닌 네 번째 클래스로 이어질 것입니다.
Doc Brown

2
@ pdr : BTW, 실제로 "클린 코드"를 읽었습니까? 밥 마틴은 간다 아주 작은 단위로 코드를 분해에까지.
Doc Brown

11

단일 책임 원칙 (Single Responsibility Principle) 또는 다른 원칙의 결정적인 구현은 없습니다. 변경 가능성이 더 높은 것을 기반으로 결정해야합니다.

@Karpie는 이전 답변을 씁니다 .

클래스는 하나만 필요하며 해당 클래스의 단일 책임은 원장을 관리하는 것입니다.

이것은 본능적 인 접근 방식이며 아마도 변경 가능성이 더 큰 것이 원장 관리의 비즈니스 논리이기 때문에 아마도 정확할 것입니다. 그렇게되면 한 클래스를 세 개보다 유지하는 것이 훨씬 쉽습니다.

그러나 그것은 사건마다 조사되고 결정되어야한다. 사소한 변화 가능성에 대한 우려를 신경 쓰지 말고 변화 할 가능성이 높은 것에 원칙을 적용하는 데 집중해야합니다. 원장 관리 논리가 다중 계층 프로세스이고 계층이 독립적으로 변경되기 쉬우거나 원칙적으로 분리되어야하는 문제 (논리 및 표시) 인 경우 별도의 클래스를 사용해야합니다. 그것은 확률 게임이다.

디자인 원칙을 과도하게 사용한다는 주제에 대해 비슷한 질문을합니다 .


1
SRP는 이해하기에는 실제로 디자인 패턴이 아닙니다.
Doc Brown

@DocBrown 물론 디자인 패턴은 아니지만 질문에 대한 토론은 매우 관련이 있습니다 ... 잘 잡았습니다. 대답을 업데이트했습니다
yannis

4

이 클래스들을 살펴 봅시다 :

  • LedgerLandingPage : 이 클래스의 역할은 원장 목록을 표시하는 것입니다. 응용 프로그램이 성장함에 따라 다른 데이터 소스의 데이터를 정렬, 필터링, 강조 표시 및 투명하게 통합하는 방법을 추가하고 싶을 것입니다.

  • ViewLedgerEntryPage : 이 페이지에는 원장이 자세히 표시됩니다. 꽤 똑바로 보인다. 데이터가 간단하다면. 여기서 원장을 무효로 할 수 있습니다. 정정 할 수도 있습니다. 또는 의견을 말하거나 파일, 영수증 또는 외부 예약 번호 등을 첨부하십시오. 그리고 편집을 허용하면 기록을 보여주고 싶을 것입니다.

  • CreateLedgerEntryPage : (가) 경우 ViewLedgerEntryPage전체 편집 기능을 가지고,이 클래스의 책임이 가능하게 또는 이해가되지 않을 수있는 "빈 항목을"편집하여 성취 할 수있다.

이 예제는 조금 멀리 보일 수도 있지만 요점은 UX에서 우리가 여기서 말하는 기능입니다. 하나의 기능은 확실히 하나의 책임입니다. 해당 기능에 대한 요구 사항이 변경 될 수 있고 서로 다른 두 가지 기능에 대한 요구 사항이 서로 반대 방향으로 바뀔 수 있으므로 처음부터 SRP를 고수하고 싶을 것입니다.
그러나 반대로 생각할 수있는 어떤 이유에서든 단일 인터페이스를 사용하는 것이 더 편리한 경우에는 항상 3 개의 클래스에 파사드를 넣을 수 있습니다.


SRP 과다 사용 과 같은 것이 있습니다 : 파일의 내용을 읽기 위해 수십 개의 클래스가 필요하다면, 그렇게하는 것으로 가정하는 것이 안전합니다.

다른 쪽에서 SRP를 보면 실제로 단일 클래스가 단일 책임을 처리해야한다고 말합니다. 그러나 책임은 추상화 수준 내에서만 존재합니다. 따라서 높은 추상화 수준의 클래스가 작업을 낮은 수준의 구성 요소로 위임하여 책임을 수행하는 것이 완벽하게 의미가 있습니다. 이는 종속성 반전을 통해 가장 잘 이루어 집니다 .


나는 심지어 당신보다이 수업의 책임을 더 잘 설명 할 수 없었을 것 같아요.
Tarun

2

그 수업이 바뀌어야 할 이유가 하나 밖에 없습니까?

모르는 경우 (아직도) 투기 적 설계 / 엔지니어링 트랩 (너무 많은 클래스, 너무 복잡한 설계 패턴 적용)을 시작했을 수 있습니다. YAGNI을 (를) 만족하지 않았습니다 . 소프트웨어 수명주기의 초기 단계 (일부) 요구 사항이 명확하지 않을 수 있습니다. 따라서 변화의 방향은 현재 보이지 않습니다. 당신이 그것을 알고 있다면, 최소한 하나의 수업으로 유지하십시오. 그러나 요구 사항과 변경 방향이 명확 해지면 현재 설계를 다시 생각하고 변경 사유를 충족시키기 위해 리팩토링을 수행해야합니다.

변경에 대한 세 가지 다른 이유가 있고 그 세 가지 클래스에 매핑되어 있음을 이미 알고 있다면 올바른 SRP 용량을 적용한 것입니다. 다시 말하지만 이것은 약간의 책임이 있습니다. 잘못되었거나 미래의 요구 사항이 예기치 않게 변경되면 현재 설계를 다시 생각하고 변경 이유를 충족시키기 위해 리팩토링을 수행해야합니다.

요점은 다음과 같습니다.

  • 변화를 위해 운전자를 주시하십시오.
  • 리팩토링 할 수있는 유연한 디자인을 선택하십시오.
  • 코드를 리팩터링 할 준비를하십시오.

이 사고 방식을 가지고 있다면 투기 적 설계 / 공학에 대한 두려움없이 코드를 형성 할 수 있습니다.

그러나 항상 요소 시간이 있습니다. 많은 변경 사항에는 필요한 시간이 없습니다. 리팩토링에는 시간과 노력이 들었습니다. 디자인을 변경해야한다는 상황이 종종 발생했지만 시간 제약으로 인해 연기해야했습니다. 이것은 일어날 것이며 피할 수 없습니다. 오버 엔지니어링 된 코드보다 엔지니어링 된 코드에서 리팩토링하는 것이 더 쉽다는 것을 알았습니다. YAGNI 는 좋은 지침을 제공합니다. 확실 하지 않은 경우 클래스 수와 디자인 패턴 적용을 최소로 유지하십시오.


1

클래스를 분할하는 이유로 SRP를 호출하는 이유는 세 가지입니다.

  • 향후 요구 사항 변경으로 인해 일부 클래스가 변경되지 않을 수 있습니다.
  • 버그를 더 빨리 격리 할 수 ​​있도록
  • 수업을 더 작게 만들기 위해

수업 나누기를 거부해야하는 세 가지 이유가 있습니다.

  • 향후 요구 사항 변경으로 인해 여러 클래스에서 동일한 변경 사항이 발생하지 않습니다.
  • 버그 찾기에는 긴 간접 경로 추적이 필요하지 않습니다.
  • 하나의 관련 클래스에서만 필요할 때 모든 사람에게 정보 나 방법을 노출시키는 캡슐화 차단 기술 (속성, 친구 등)에 저항

귀하의 사례를보고 변경 사항을 상상할 때 일부 클래스는 여러 클래스를 변경하게됩니다 (예 : 필드를 원장에 추가하면 세 가지를 모두 변경해야 함). 예를 들어 원장 목록에 정렬을 추가하는 것은 아닙니다 새 원장 항목을 추가 할 때 유효성 검사 규칙을 변경합니다. 동료의 반응은 버그를 찾을 위치를 알기가 어렵 기 때문일 수 있습니다. 원장 목록에 문제가있는 경우 잘못 추가되었거나 목록에 문제가있는 것입니까? 요약과 세부 사항간에 불일치가있는 경우 어떤 것이 잘못 되었습니까?

또한 동료가 세 가지 클래스를 모두 변경해야한다는 요구 사항의 변경이 하나만 변경하면되는 변경보다 훨씬 가능성이 높다고 생각할 수도 있습니다. 그것은 정말 유용한 대화가 될 수 있습니다.


다른 관점을 보는 것이 좋습니다
Tarun

0

원장이 db의 테이블 인 경우이 thress 작업을 하나의 클래스 (예 : DAO)에 넣는 것이 좋습니다. 원장이 더 많은 논리를 가지고 있고 db의 테이블이 아닌 경우이 작업을 수행하기 위해 더 많은 클래스를 만들어야한다고 제안합니다 (2 또는 thress 클래스 일 수 있음) 고객을 위해 간단하게 사용할 수있는 파사드 클래스를 제공합니다.


원장은 UI의 기능이며 다른 페이지에서 수행 할 수있는 관련 작업이 많이 있습니다.
Tarun

0

IMHO 클래스를 전혀 사용해서는 안됩니다. 여기에는 데이터 추상화가 없습니다. 원장은 구체적인 데이터 유형입니다. 그것들을 조작하고 표시하는 방법은 기능과 절차입니다. 날짜 형식화와 같이 공유 할 공통적으로 사용되는 기능이 많이 있습니다. 표시 및 선택 루틴의 클래스에서 데이터를 추상화하고 숨길 수있는 공간은 거의 없습니다.

원장 편집을 위해 클래스에서 상태를 숨기는 경우가 있습니다.

SRP를 남용하는지 여부에 관계없이 더 근본적인 것을 남용하고 있습니다. 추상화를 과도하게 사용하고 있습니다. 신화적인 개념 인 OO에 의해 야기 된 전형적인 문제는 정상적인 개발 패러다임입니다.

프로그래밍은 90 % 구체적입니다. 데이터 추상화 사용은 드물고 신중하게 설계해야합니다. 반면에 언어 세부 사항과 알고리즘 선택은 연산의 의미와 분리되어야하기 때문에 기능적 추상화는 더 일반적이어야합니다.


-1

클래스는 하나만 필요하며 해당 클래스의 단일 책임은 원장관리하는 것 입니다.


2
나에게 '... 관리자'수업은 일반적으로 더 이상 분류하기 위해 귀찮게 할 수 없다는 것을 보여줍니다. 수업은 무엇입니까? 프레젠테이션, 끈기?
sebastiangeiger

-1. 3 개 이상의 유스 케이스를 1 개의 클래스에 넣는 것은 SRP가 피하려고하는 것과 정확히 같은 이유입니다.
Doc Brown

@sebastiangeiger 그렇다면 OP의 세 가지 수업은 무엇입니까? 프레젠테이션 또는 지속성?
sevenseacat

@Karpie 저는 솔직히 프레젠테이션을 기대하고 있습니다. 물론, 예제는 실제로 좋지는 않지만 웹 사이트의 '... 페이지'가보기와 비슷한 것을하고 있다고 기대합니다.
sebastiangeiger

2
실제로, 당신은 전체 응용 프로그램에 대해 하나의 클래스 만 필요하며, 사용자를 행복하게 하는 단일 책임 ;-)
Péter Török
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.