단일 책임 원칙을 새 코드에 적용 할 수 있습니까?


20

이 원칙은 변경해야 할 한 가지 이유가있는 모듈로 정의됩니다 . 내 질문은, 코드가 실제로 변경되기 시작할 때까지 이러한 변경 이유를 알 수 없다는 것입니다. 거의 모든 코드에는 변경 될 수 있는 여러 가지 이유 있지만, 이러한 코드를 모두 예상하고이를 염두에두고 코드를 설계하려고하면 코드 품질이 매우 떨어질 수 있습니다. 코드 변경 요청이 들어올 때 실제로 SRP를 적용하기 시작하는 것이 더 좋은 생각입니까? 보다 구체적으로, 하나의 코드가 하나 이상의 이유로 인해 두 번 이상 변경되었을 때, 하나 이상의 변경 이유가 있음을 증명합니다. 변화의 이유를 추측하려고 시도하는 것은 매우 민첩한 것 같습니다.

예를 들어 문서를 인쇄하는 코드가 있습니다. PDF로 인쇄하도록 변경 요청한 다음 문서에 다른 형식을 적용하기 위해 변경을 요청합니다. 이 시점에서 변경해야하는 단일 이유 (및 SRP 위반)에 대한 증거가 있으며 적절한 리팩토링을 수행해야합니다.


6
@Frank-실제로 일반적으로 그렇게 정의되어 있습니다-예를 들어 en.wikipedia.org/wiki/Single_responsibility_principle
Joris Timmermans

1
당신이 그것을 표현하는 방식은 SRP의 정의를 이해하는 방식이 아닙니다.
Pieter B

2
모든 코드 줄에는 최소한 두 가지 변경 이유가 있습니다. 버그를 유발하거나 새로운 요구 사항을 방해합니다.
Bart van Ingen Schenau

1
@BartvanIngenSchenau : LOL ;-)이 방법으로 보면 SRP를 어디에도 적용 할 수 없습니다.
Doc Brown

1
@DocBrown : SRP를 소스 코드 변경과 연결하지 않으면 가능합니다. 예를 들어, 당신은 단어를 사용하지 않고 클래스 / 함수가 한 문장으로 수행하는 작업에 대한 전체 계정을 줄 수있는 것으로 SRP를 해석 하고 (그 제한을 해결하기 위해 어떤 족제비 표현을).
Bart van Ingen Schenau

답변:


27

물론 YAGNI 원칙에 따라 SRP를 실제로 적용하기 전에 SRP를 적용하지 않아도됩니다. 그러나 스스로 자문해야 할 질문은 SRP를 먼저 적용해야하고 실제로 코드를 변경해야하는 경우에만 적용해야합니까?

필자의 경험에 따르면 SRP 를 적용하면 코드의 특정 변경 사항을 적용 할 위치방법 을 찾아야 할 때 훨씬 빨리 혜택을 얻을 수 있습니다. 이 작업을 위해서는 기존 기능과 클래스를 읽고 이해해야합니다. 모든 함수와 클래스에 특정 책임이있는 경우 훨씬 쉬워집니다. 따라서 IMHO는 코드를 더 쉽게 읽을 수있게 할 때마다, 기능이 작고 자체적으로 설명 할 때마다 SRP를 적용해야합니다. 따라서 대답은 yes 입니다. 새 코드에도 SRP를 적용하는 것이 좋습니다.

당신의 인쇄 코드가 문서를 읽을 때 예를 들어, 문서를 포맷 하고 특정 장치에 결과를 출력,이 3 개 명확한 분리의 책임입니다. 따라서 적어도 3 가지 기능을 만들어 이름에 따라 지정하십시오. 예를 들면 다음과 같습니다.

 void RunPrintWorkflow()
 {
     var document = ReadDocument();
     var formattedDocument = FormatDocument(document);
     PrintDocumentToScreen(formattedDocument);
 }

이제 문서 서식을 변경하거나 PDF로 인쇄 할 다른 형식을 변경 해야하는 새로운 요구 사항이 생겼을 때 코드에서 이러한 기능 또는 위치를 변경 사항을 적용해야하는 위치와 그보다 더 중요한 부분을 정확히 알아야 합니다 .

당신이 함수에 올 때마다 함수가 "너무 많이"않기 때문에 그래서, 당신은 이해하지 않고, 변화를 적용하는 경우와, 경우에 당신이 확실하지 않은 별도의 작은 기능에 기능을 리팩토링하는 것이 좋습니다. 무언가를 바꿀 때까지 기다리지 마십시오. 코드는 변경된 것보다 10 배 더 자주 읽으며 더 작은 기능은 훨씬 쉽게 읽을 수 있습니다. 함수가 특정 복잡성을 가질 때 내 경험에, 당신은 할 수 항상 변화가 미래에 올 것이다 알 독립적 인 다른 책임에 기능을 분할합니다. 밥 마틴은 일반적으로 한 단계 더 나아갑니다. 아래의 의견에서 내가 준 링크를 참조하십시오.

편집 : 귀하의 의견 : 위의 예에서 외부 기능의 주요 책임은 특정 장치로 인쇄하거나 문서를 형식화하는 것이 아니라 인쇄 워크 플로우통합하는 것입니다 . 따라서 외부 기능의 추상화 수준에서 "문서를 더 이상 형식화하지 않아야합니다"또는 "문서를 인쇄 대신 메일로 보내야 함"과 같은 새로운 요구 사항은 "동일한 이유", 즉 "인쇄 워크 플로가 변경되었습니다"입니다. 우리가 그런 것들에 대해 이야기한다면, 올바른 추상화 수준 을 고수하는 것이 중요합니다 .


나는 일반적으로 항상 TDD로 개발하므로 내 예제에서는 테스트 할 수 없기 때문에 실제로 모든 논리를 하나의 모듈로 유지할 수 없었습니다. 이것은 단지 TDD의 부산물이며 의도적으로 SRP를 적용하기 때문이 아닙니다. 내 모범은 상당히 명확하고 별도의 책임이 있었으므로 좋은 모범이 아닐 수도 있습니다. 내가 묻는 것은 새로운 코드를 작성하고 분명히 말할 수 있습니까? 그렇습니다 .SRP를 위반하지 않습니까? '변경 이유'가 본질적으로 비즈니스에 의해 정의되지 않습니까?
SeeNoWeevil

3
@ thecapsaicinkid : 예, (적어도 즉시 리팩토링을 통해) 가능합니다. 그러나 매우 작은 기능을 사용할 수 있으며 모든 프로그래머가이를 좋아하는 것은 아닙니다. 다음 예를 참조하십시오. sites.google.com/site/unclebobconsultingllc/…
Doc Brown

변경 사유를 예상하여 SRP를 적용한 경우, 귀하의 예에서 여전히 단일 사유 변경 이상이 있다고 주장 할 수 있습니다. 기업은 더 이상 문서를 포맷하지 않기로 결정한 다음 나중에 인쇄 대신 전자 메일로 보내기를 원할 수 있습니다. 편집 : 링크를 읽으십시오. 특히 최종 결과가 마음에 들지 않지만 '더 이상 추출 할 수 없을 때까지 추출하십시오'는 훨씬 더 의미가 있으며 '변경해야 할 한 가지 이유'보다 모호하지 않습니다. 실용적이지는 않습니다.
SeeNoWeevil

1
@thecapsaicinkid : 내 편집을 참조하십시오. 외부 기능의 주요 책임은 특정 장치로 인쇄하거나 문서를 포맷하는 것이 아니라 인쇄 작업 과정을 통합하는 것입니다. 이 워크 플로가 변경되면 이것이 기능이 변경되는 유일한 이유입니다.
Doc Brown

올바른 추상화 수준을 고수하는 것에 대한 귀하의 의견은 내가 놓친 것 같습니다. 예를 들어 'JSON 배열에서 데이터 구조를 생성합니다'라고 설명하는 클래스가 있습니다. 하나의 책임 인 것 같습니다. JSON 배열에서 객체를 반복하고 POJO에 매핑합니다. 내 설명과 동일한 추상화 수준을 고수하면 'JSON이 객체에 매핑하는 방법'을 변경해야하는 이유가 두 가지 이상이라고 주장하기가 어렵습니다. 추상적 이기 때문에 날짜 필드 변경 사항을 매핑하는 방법, 숫자 값을 일로 매핑하는 방법 등 여러 가지 이유가 있다고 주장 할 수 있습니다.
SeeNoWeevil

7

당신이 SRP를 오해하고 있다고 생각합니다.

변경의 단일 이유는 코드 변경이 아니라 코드의 기능 때문입니다.


3

SRP의 정의가 "변경해야 할 한 가지 이유가있다"는 것이 바로 이런 이유로 오해의 소지가 있다고 생각합니다. 액면가를 정확히 고려하십시오. 단일 책임 원칙에 따르면 클래스 또는 함수에는 하나의 책임이 있어야합니다. 변경해야 할 이유가 하나만있는 것은 시작하기 위해 한 가지만하는 것의 부작용입니다. 앞으로 코드가 어떻게 변경 될지 알지 못하면 코드에서 단일 책임을 수행하려고 노력할 이유가 없습니다.

이런 종류의 가장 좋은 단서 중 하나는 클래스 또는 함수 이름을 선택할 때입니다. 클래스의 이름을 명확하게 알 수 없거나 이름이 특히 길거나 복잡하거나 이름이 "관리자"또는 "유틸리티"와 같은 일반 용어를 사용하는 경우 SRP를 위반 한 것일 수 있습니다. 마찬가지로 API를 문서화 할 때 설명하는 기능에 따라 SRP를 위반하는 경우 API가 빠르게 드러날 것입니다.

물론 SRP에는 나중에 프로젝트에서 알 수없는 뉘앙스가 있습니다. 단일 책임은 2 또는 3으로 나타났습니다. SRP를 구현하기 위해 리팩토링해야하는 경우입니다. 그렇다고 변경 요청을받을 때까지 SRP를 무시해야한다는 의미는 아닙니다. 그것은 SRP의 목적을 무너 뜨립니다!

예를 직접 들으려면 인쇄 방법을 문서화 해보십시오. 당신이 말하는 것 "이 방법은 인쇄 데이터의 형식을 프린터로 전송"그 경우 당신을 얻을 것입니다 : 하나의 책임이 아니다 즉, 두 개의 책임의 그 : 포맷 프린터로 전송가. 이것을 인식하고 그것들을 두 개의 함수 / 클래스로 나누면, 변경 요청이 왔을 때, 각 섹션이 변경 될 이유는 이미 한 가지 일 것입니다.


3

예를 들어 문서를 인쇄하는 코드가 있습니다. PDF로 인쇄하도록 변경 요청한 다음 문서에 다른 형식을 적용하기 위해 변경을 요청합니다. 이 시점에서 변경해야하는 단일 이유 (및 SRP 위반)에 대한 증거가 있으며 적절한 리팩토링을 수행해야합니다.

나는 그러한 변화에 적응하기 위해 코드를 적응시키는 데 너무 많은 시간을 소비함으로써 너무나 많은 시간을 보냈다. 그냥 바보 같은 PDF를 인쇄하는 대신.

코드 축소를위한 리 팩터

일회용 패턴은 코드 팽창을 만들 수 있습니다. 개별적으로 이해되지 않는 가비지 코드를 생성하는 작은 특정 클래스로 패키지가 오염되는 경우. 인쇄 부분에 어떻게 도달하는지 이해하려면 수십 개의 소스 파일을 열어야합니다. 게다가 실제 인쇄를 수행하는 10 줄의 코드를 실행하기 위해 수천 줄의 코드가 없다면 수백 개가 될 수 있습니다.

불즈 아이 만들기

단일 사용 패턴은 소스 코드를 줄이고 코드 재사용을 개선하기위한 것입니다. 전문화 및 특정 구현을 작성하기위한 것입니다. bullseye소스 코드에서 일종의go to specific tasks . 인쇄에 문제가 있었을 때 인쇄 할 위치를 정확히 알고있었습니다.

단일 사용이 모호한 프랙 처를 의미하지는 않습니다

예, 이미 문서를 인쇄하는 코드가 있습니다. 예, 이제 PDF도 인쇄하도록 코드를 변경해야합니다. 예, 이제 문서 형식을 변경해야합니다.

usage크게 바뀌 었습니까?

리팩토링으로 인해 소스 코드의 섹션이 지나치게 일반화되는 경우 원래 의도 printing stuff가 더 이상 명시 적이 지 않을 정도로 소스 코드에서 모호한 파쇄를 만들었습니다.

새로운 사람이 이것을 빨리 알아낼 수 있을까요?

이해하기 쉬운 조직에서 항상 소스 코드를 유지하십시오.

워치 메이커가되지 마십시오

개발자가 접안 렌즈를 착용하는 것을 너무나 자주 보았고, 작은 디테일에 초점을 두어 다른 사람이 조각을 다시 조립할 수 없을 정도로 집중했습니다.

여기에 이미지 설명을 입력하십시오


2

변경 이유는 궁극적으로 응용 프로그램이 실행되는 환경에 대한 사양이나 정보가 변경되기 때문입니다. 따라서 단일 책임 원칙은 각 구성 요소 (클래스, 함수, 모듈, 서비스 ...)를 작성하여 가능한 한 적은 사양 및 실행 환경을 고려해야한다는 것입니다.

구성 요소를 작성할 때 사양과 환경을 알고 있으므로 원칙을 적용 할 수 있습니다.

문서를 인쇄하는 코드 예제를 고려하십시오. 문서가 PDF로 끝나는 것을 고려하지 않고 레이아웃 템플릿을 정의 할 수 있는지 고려해야합니다. 당신은 할 수 있습니다, 그래서 SRP는 당신에게해야한다고 말하고 있습니다.

물론 YAGNI는하지 말아야한다고 말합니다. 디자인 원칙 간의 균형을 찾아야합니다.


2

플랩은 올바른 방향으로 향합니다. "단일 책임 원칙"은 원래 절차에 적용되었습니다. 예를 들어 Dennis Ritchie는 함수가 한 가지 작업을 수행하고 잘 수행해야한다고 말합니다. 그런 다음 C ++에서 Bjarne Stroustrup은 클래스가 한 가지 일을하고 잘해야한다고 말합니다.

경험적으로,이 두 규칙은 공식적으로 서로 관련이 없거나 거의 관련이 없습니다. 그들은 프로그래밍 언어로 표현하기 편리한 것에 만 적합합니다. 글쎄, 그건 뭔가입니다. 그러나 그것은 플럽이 운전하는 것과는 상당히 다른 이야기입니다.

최신 (즉, 민첩 및 DDD) 구현은 프로그래밍 언어가 표현할 수있는 것보다 비즈니스에 중요한 것에 더 중점을 둡니다. 놀라운 부분은 프로그래밍 언어가 아직 따라 잡지 않았다는 것입니다. 기존 FORTRAN과 유사한 언어는 주요 개념 모델 인 카드 리더를 통과 할 때 각 카드에 적용되는 프로세스 또는 각 인터럽트에 수반되는 처리 (C에서와 같이)에 해당하는 책임을 포착합니다. 그런 다음 ADT 언어가 생겨서 DDD 사람들이 나중에 중요하게 재발견 할 내용을 포착 할 정도로 발전했습니다 (Jim Neighbors는 1968 년까지이 중 대부분을 파악하고 게시하고 사용했지만) . (모듈이 아닙니다.)

이 단계는 진자 스윙보다 진화가 적습니다. 진자가 데이터로 전환함에 따라 FORTRAN 고유의 사용 사례 모델링을 잃었습니다. 주요 초점이 화면의 데이터 또는 모양과 관련되어 있으면 좋습니다. PowerPoint와 같은 프로그램이나 간단한 작업에 적합한 모델입니다.

잃어버린 것은 시스템 책임 입니다. 우리는 DDD의 요소를 판매하지 않습니다. 그리고 우리는 잘 수업 방법이 없습니다. 우리는 시스템 책임을 판매합니다. 어떤 수준에서는 단일 책임 원칙을 중심으로 시스템을 설계해야합니다.

따라서 Rebecca Wirfs-Brock과 같은 사람들이나 클래스 메소드에 대해 이야기했던 사람들을 보면, 우리는 이제 유스 케이스 측면에서 이야기하고 있습니다. 그것이 우리가 판매하는 것입니다. 그것들은 시스템 운영입니다. 유스 케이스에는 단일 책임이 있어야합니다. 유스 케이스는 거의 건축 단위가 아닙니다. 그러나 모든 사람들이 그것을 가장하려고했습니다. 예를 들어 SOA 사람들을 감시하십시오.

이것이 제가 Trygve Reenskaug의 DCI 아키텍처에 대해 기쁘게 생각하는 이유입니다. 위의 린 아키텍처 (Lean Architecture) 책에 설명되어 있습니다. 그것은 마지막으로 "단일 책임"에 대한 자의적이고 신비한 순종이었던 것에 대한 실질적인 입장을 제시한다. 이 키는 인간의 정신 모델과 관련이 있습니다 : 최종 사용자가 먼저, 프로그래머가 둘째입니다. 비즈니스 문제와 관련이 있습니다. 그리고 거의 모든 상황에서 플랩이 우리에게 도전 할 때 변화를 캡슐화합니다.

우리가 알고있는 단일 책임 원칙은 원래의 시대에서 남은 공룡이거나 우리가 이해의 대용품으로 사용하는 취미 말입니다. 멋진 소프트웨어를 만들려면 이러한 취미 말 몇 개를 남겨 두어야합니다. 그리고 그것은 상자 밖으로 생각해야합니다. 일을 간단하고 이해하기 쉽게 유지하는 것은 문제가 간단하고 이해하기 쉬운 경우에만 작동합니다. 나는 그 솔루션에별로 관심이 없다. 그것들은 전형적인 것이 아니며 도전이있는 곳이 아니다.


2
당신이 쓴 것을 읽을 때, 어딘가에서 나는 당신이 말하는 것을 완전히 잃어 버렸습니다. 좋은 답변은 질문을 숲 속의 럼블의 출발점으로 취급하지 않고 모든 글을 연결하는 명확한 주제로 취급합니다.
Donal Fellows

1
아, 당신은 저의 옛 관리자 중 한 사람입니다. "우리는 그것을 이해하고 싶지 않다 : 그것을 개선하고 싶다!" 여기서 중요한 주제는 원칙 중 하나입니다. "SRP"의 "P"입니다. 아마도 올바른 질문이라면 질문에 직접 대답했을 것입니다. 질문을 제기 한 사람과 함께 취할 수 있습니다.
대처

여기 어딘가에 묻힌 좋은 대답이 있습니다. 내 생각 엔 ..
RubberDuck

0

그렇습니다. 단일 책임 원칙은 새 코드에 적용해야합니다.

그러나! 책임은 무엇입니까?

"보고서를 인쇄하는 것이 책임"입니까? 대답은 "아마도"라고 믿습니다.

SRP의 정의를 "단일 변경해야하는 이유"로 사용하려고합니다.

보고서를 인쇄하는 기능이 있다고 가정하십시오. 두 가지 변경 사항이있는 경우 :

  1. 보고서에 검정색 배경이 있어야하므로 해당 기능을 변경하십시오.
  2. pdf로 인쇄해야하므로 해당 기능을 변경하십시오.

그런 다음 첫 번째 변경은 "보고서 스타일 변경"이고 다른 하나는 "보고서 출력 형식 변경"입니다. 이제는 서로 다른 두 가지 기능을 사용해야합니다.

그러나 두 번째 변경 사항은 다음과 같습니다.

2b. 보고서에 다른 글꼴이 필요하므로 해당 기능을 변경하십시오.

두 가지 변경 사항 모두 "보고서 스타일 변경"이라고 말하면 하나의 기능 만 유지할 수 있습니다.

그래서 어디로 우리를 떠나? 평소와 같이 일을 간단하고 이해하기 쉽게 유지해야합니다. 배경색을 변경하면 20 줄의 코드를 의미하고 글꼴을 변경하면 20 줄의 코드를 의미하는 경우 다시 두 가지 기능을 수행하십시오. 각 줄이 한 줄이면 한 줄로 유지하십시오.


0

새 시스템을 설계 할 때는 수명 동안 수행해야 할 변경 종류와 배치중인 아키텍처에 얼마나 많은 비용이 드는지 고려하는 것이 좋습니다. 시스템을 모듈로 나누는 것은 비용이 많이 드는 잘못된 결정입니다.

좋은 정보원은 비즈니스 도메인 전문가의 정신 모델입니다. 문서, 서식 및 pdf를 예로 들어 보겠습니다. 도메인 전문가는 문서 서식 파일을 사용하여 편지 서식을 지정할 수 있습니다. 고정식 또는 Word 또는 기타로 제공됩니다. 코딩을 시작하기 전에이 정보를 검색하여 디자인에 사용할 수 있습니다.

Coplien의 Lean Architecture


0

"인쇄"는 MVC의 "보기"와 매우 유사합니다. 물체의 기본을 이해하는 사람이라면 누구나 이해할 것입니다.

그것은이다 시스템 의 책임. 프린터 (보기), 인쇄 대상 (모듈) 및 프린터 요청 및 옵션 (컨트롤러의)을 포함하는 메커니즘 (MVC)으로 구현됩니다.

이것을 클래스 또는 모듈 책임으로 현지화하려는 것은 무의미하며 30 세의 사고를 반영합니다. 그 이후로 우리는 많은 것을 배웠으며, 그것은 문학과 성숙한 프로그래머의 코드에서 충분히 입증되었습니다.


0

코드 변경 요청이 들어올 때 실제로 SRP를 적용하기 시작하는 것이 더 좋은 생각입니까?

이상적으로는 코드의 여러 부분에 대한 책임이 무엇인지 이미 잘 알고있을 것입니다. 첫 번째 본능에 따라 책임을 분담하십시오. 사용하려는 라이브러리가 무엇을 원하는지 고려하십시오 (작업 위임, 책임, 라이브러리에 대한 라이브러리는 실제로 작업을 수행 할 수 있다면 일반적으로 수행하는 것이 좋습니다) ). 그런 다음 변화하는 요구 사항에 따라 책임에 대한 이해를 개선하십시오. 처음에 시스템을 더 잘 이해할수록 책임 할당을 근본적으로 변경해야 할 필요성이 줄어 듭니다 (때때로 책임이 하위 책임으로 가장 잘 구분되어 있음을 발견 할 수도 있음).

걱정하는 데 오랜 시간이 걸리는 것이 아닙니다. 코드의 핵심 기능은 나중에 변경 될 수 있다는 것입니다. 처음에는 완벽하게 적용 할 필요가 없습니다. 미래에 실수를 줄일 수 있도록 시간이 지남에 따라 어떤 종류의 형태 책임이 있는지 배우는 데 더 노력하십시오.

예를 들어 문서를 인쇄하는 코드가 있습니다. PDF로 인쇄하도록 변경 요청한 다음 문서에 다른 형식을 적용하기 위해 변경을 요청합니다. 이 시점에서 변경해야하는 단일 이유 (및 SRP 위반)에 대한 증거가 있으며 적절한 리팩토링을 수행해야합니다.

이것은 코드의“인쇄”에 대한 전반적인 책임이 하위 책임을 가지며 조각으로 나뉘어 야 함을 나타냅니다. 이는 SRP 자체를 위반하는 것이 아니라 파티셔닝 (아마도“포맷”및“렌더링”하위 작업)이 필요하다는 표시입니다. 구현을 보지 않고 하위 작업 내에서 무슨 일이 일어나고 있는지 이해할 수 있도록 이러한 책임을 명확하게 설명 할 수 있습니까? 가능하다면 합리적인 분열 일 가능성이 높습니다.

간단한 실제 예를 보면 더 명확해질 수 있습니다. 의 sort()유틸리티 방법을 고려해 보자 java.util.Arrays. 무엇을합니까? 배열을 정렬하는 것이 전부입니다. 그것은 요소를 출력하지 않고, 가장 도덕적으로 적합한 멤버를 찾지 못하고, 딕시를 휘파람시키지 않습니다 . 단지 배열을 정렬합니다. 방법을 몰라도됩니다. 정렬은 그 방법의 책임입니다. (실제로, 원시적 유형과 관련하여 다소 추악한 기술적 이유로 Java에는 많은 정렬 메소드가 있습니다. 모두 동등한 책임이 있기 때문에주의를 기울일 필요는 없습니다.)

메소드, 클래스, 모듈을 명확하게 지정하여 인생에서 분명하게 지정하십시오. 그것은 당신이 한 번에 이해해야 할 양을 유지하고, 결국 큰 시스템의 설계와 유지를 처리 ​​할 수있게합니다.

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