각 반에 하나의 책임이 있는지 확인하십시오. 왜 그렇습니까?


37

Microsoft 문서, Wikipedia SOLID 프린시 페 기사 또는 대부분의 IT 설계자에 따르면 각 클래스마다 하나의 책임 만 갖도록해야합니다. 모든 사람이이 규칙에 동의하는 것 같으면이 규칙의 이유에 대해 아무도 동의하지 않는 것 같아 이유를 알고 싶습니다.

어떤 사람들은 더 나은 유지 보수를 인용하고, 어떤 사람들은 쉬운 테스트를 제공하거나 클래스를보다 강력하거나 안전하게 만듭니다. 무엇이 정확하고 실제로 무엇을 의미합니까? 유지 보수를 개선하고 테스트를 쉽게하거나 코드를 더 강력하게 만드는 이유는 무엇입니까?



1
나는 당신도 관련이 있을지도 모른다는 질문을했다 : 수업의 실제 책임은 무엇인가?
Pierre Arlaud

3
단일 책임에 대한 모든 이유가 정확할 수 있습니까? 유지 보수가 쉬워집니다. 테스트가 더 쉬워집니다. 그것은 클래스를보다 강력하게 만듭니다 (일반적으로).
Martin York

2
같은 이유로 당신은 모두 수업이 있습니다.
Davor Ždralo

2
나는 항상이 진술에 문제가 있었다. "단일 책임"이 무엇인지 정의하는 것은 매우 어렵습니다. 단일 책임은 "1 + 1 = 2 확인"에서 "기업 은행 계좌로 입금 및 지불 된 모든 돈의 정확한 로그를 유지"하는 것까지 다양합니다.
Dave Nay

답변:


57

모듈성. 적절한 언어는 코드 조각을 서로 붙일 수있는 방법을 제공하지만 프로그래머가 소스에서 수술을 수행하지 않고도 큰 코드 조각을 풀 수있는 일반적인 방법은 없습니다. 많은 작업을 하나의 코드 구성으로 방해함으로써 자신과 다른 사람들이 다른 방식으로 조각을 결합 할 수있는 기회를 빼앗아 한 조각의 변경 사항이 다른 조각에 영향을 줄 수있는 불필요한 종속성을 도입합니다.

SRP는 클래스와 마찬가지로 함수에도 적용 할 수 있지만 주류 OOP 언어는 함수를 함께 붙이는 데 상대적으로 열악합니다.


12
추상화를 작성하는 더 안전한 방법으로 기능 프로그래밍을 언급 한 +1
logc

> 그러나 주류 OOP 언어는 기능을 함께 붙이는 데 상대적으로 열악합니다. <OOP 언어는 기능이 아니라 메시지와 관련이 있기 때문입니다.
Jeff Hubbard

@ JeffHubbard 나는 그것이 C / C ++을 따랐기 때문이라고 생각합니다. 함수와 상호 배타적 인 객체에 대해서는 아무것도 없습니다. 지옥, 객체 / 인터페이스는 단지 함수의 기록 / 구조입니다. 기능이 중요하지 않은 척하는 것은 이론과 실습 모두에서 보증되지 않습니다. 어쨌든 피할 수는 없습니다. "Runnables", "Factories", "Providers"및 "Actions"를 포기하거나 소위 전략 및 템플릿 메소드 "패턴"을 사용해야합니다. 동일한 작업을 수행하는 데 필요한 불필요한 상용구.
Doval

@Doval 아니요, OOP가 메시지와 관련이 있다는 것을 의미합니다. 즉, 주어진 언어가 함수를 함께 붙일 때 함수형 프로그래밍 언어보다 낫거나 나쁜 것은 아닙니다 . 언어의 주요 관심사가 아니라는 것 입니다.
Jeff Hubbard

기본적으로, 당신의 언어가 OOP를 더 쉽고 / 더 좋고 / 빠르고 / 더 강하게 만드는 것에 관한 것이라면, 함수들이 잘 어울리는 지 여부는 당신이 집중하는 것이 아닙니다.
Jeff Hubbard

30

더 나은 유지 관리, 쉬운 테스트, 빠른 버그 수정은 SRP를 적용한 결과입니다. Robert C. Matin이 말한 주된 이유는 다음과 같습니다.

클래스에는 변경해야 할 단 하나의 이유가 있어야합니다.

다시 말해, SRP는 변화 지역성을 높입니다 .

SRP는 또한 DRY 코드를 촉진합니다. 책임이 하나 뿐인 수업이있는 한 원하는 곳 어디에서나 사용할 수 있습니다. 두 가지 책임이있는 수업이 있지만 그 중 하나만 필요하고 두 번째는 방해하는 경우 두 가지 옵션이 있습니다.

  1. 클래스를 다른 클래스에 복사하여 붙여넣고 다른 다중 책임 돌연변이 체를 만들 수도 있습니다 (기술 부채를 약간 올립니다).
  2. 수업을 나눠서 처음에해야 할대로 만들면 원래 수업의 광범위한 사용으로 인해 비용이 많이들 수 있습니다.

1
+1 SRP를 DRY에 연결합니다.
Mahdi

21

특정 문제를 해결하는 코드를 쉽게 만들 수 있습니다. 나중에 문제를 안전하게 변경할 수 있도록하면서 해당 문제를 해결하는 코드를 작성하는 것이 더 복잡합니다. SOLID는 코드 개선을위한 일련의 사례를 제공합니다.

어느 쪽이 옳은가 : 세 가지 모두. 모든 책임은 단일 책임을 사용해야하며이를 사용해야하는 이유입니다.

그들이 무엇을 의미하는지에 관해서 :

  • 유지 관리가 향상되면 변경하기 쉽고 자주 변경되지 않습니다. 코드가 적고 특정 코드에 중점을두기 때문에 클래스와 관련이없는 것을 변경해야하는 경우 클래스를 변경할 필요가 없습니다. 또한 클래스를 변경해야하는 경우 공용 인터페이스를 변경할 필요가없는 한 해당 클래스 만 걱정하면됩니다.
  • 손쉬운 테스트는 테스트가 적고 설정이 적다는 의미입니다. 클래스에는 움직이는 부분이 많지 않으므로 클래스에서 발생할 수있는 실패 횟수가 적으므로 테스트해야 할 경우가 줄어 듭니다. 설정할 개인 필드 / 멤버 수가 줄어 듭니다.
  • 위의 두 가지로 인해 변경이 적고 실패가 적은 클래스가 생겨 더 강력 해집니다.

원칙에 따라 잠시 동안 코드를 작성하고 나중에 해당 코드를 다시 방문하여 일부 변경을 수행하십시오. 당신은 그것이 제공하는 큰 장점을 볼 수 있습니다.

각 클래스에 대해 동일한 작업을 수행하고 SRP를 준수하는 더 많은 클래스로 끝납니다. 연결 관점에서 구조를 더 복잡하게 만들지 만 각 클래스의 단순성은 그 구조를 정당화합니다.


나는 여기에 만들어진 요점이 정당하다고 생각하지 않는다. 특히 클래스를 단순화하면 다른 클래스가 더 복잡해져 코드 전체에 아무런 영향을 미치지 않는 제로섬 게임을 피할 수 있습니까? @Doval의 답변 이이 문제를 해결 한다고 생각 합니다.

2
모든 수업에 동일하게 적용됩니다. SRP를 준수하는 더 많은 클래스로 끝납니다. 연결 관점에서 구조를 더 복잡하게 만듭니다. 그러나 각 클래스의 단순성은 그것을 정당화합니다. 그러나 @Doval의 답변이 마음에 듭니다.
미야모토 아키라

나는 당신이 당신의 대답에 그것을 추가해야한다고 생각합니다.

4

여기에는 단일 책임 원칙이 모범 사례라는 주장을 뒷받침하는 주장이 있습니다. 또한보다 자세한 추론을 읽을 수있는 더 많은 문헌에 대한 링크를 제공합니다.

  • 더 나은 유지 보수 : 이상적으로는 시스템의 기능이 변경되어야 할 때마다 변경해야 할 클래스가 하나만 있습니다. 클래스와 책임을 명확하게 매핑하면 프로젝트에 참여한 모든 개발자가이 클래스를 식별 할 수 있습니다. (@ MaciejChałapuk가 언급했듯이 Robert C. Martin, "Clean Code"참조 )

  • 보다 쉬운 테스트 : 이상적으로, 클래스는 가능한 최소한의 퍼블릭 인터페이스를 가져야하며 테스트는이 퍼블릭 인터페이스 만 처리해야합니다. 명확하게 테스트 할 수없는 경우, 클래스의 많은 부분이 비공개이므로 클래스에 너무 많은 책임이 있으며 더 작은 서브 클래스로 분할해야한다는 분명한 신호입니다. 이것은 '공개'또는 '비공개'반원이없는 언어에도 적용됩니다. 공용 인터페이스가 작다는 것은 클라이언트 코드가 클래스의 어느 부분을 사용해야하는지 매우 명확하다는 것을 의미합니다. (자세한 내용은 Kent Beck, "Test-Driven Development" 를 참조하십시오.)

  • 강력한 코드 : 코드가 잘 작성되어있어 자주 실패하지 않습니다. 그러나 모든 코드와 마찬가지로 궁극적 인 목표는 머신과 통신하는 것이 아니라 동료 개발자와 통신하는 것입니다 ( Kent Beck, "구현 패턴" , 1 장 참조) 명확한 코드베이스는 추론하기 쉬우므로 버그가 덜 발생합니다. 버그를 발견하고 수정하는 데 걸리는 시간이 줄어 듭니다.


비즈니스상의 이유로 변경해야 할 사항이 있다면 SRP를 통해 하나 이상의 클래스를 수정해야합니다. 수업이 변경되면 한 가지 이유 만 같지 않다고 말하면 변경이 있으면 하나의 수업에만 영향을 미칩니다.
Bastien Vandamme

@ B413 : 나는 것을 의미하지 않았다 어떤 변화가 하나의 클래스에 하나의 변화를 의미합니다. 단일 비즈니스 변경 사항을 준수하기 위해 시스템의 많은 부분을 변경해야 할 수도 있습니다. 그러나 아마도 당신은 요점을 가지고 있으며 "시스템의 기능이 바뀔 때마다"라고 써야합니다.
logc

3

여러 가지 이유가 있지만 필자가 좋아하는 것은 많은 초기 UNIX 프로그램에서 사용 된 접근 방식입니다. 한 가지만 잘 수행하십시오. 한 가지로 그렇게하는 것은 어렵고, 더 많은 일을하려고하면 어려워집니다.

또 다른 이유는 부작용을 제한하고 통제하는 것입니다. 나는 조합 커피 메이커 도어 오프너를 좋아했습니다. 불행히도, 방문객이 있었을 때 커피가 넘치게되었습니다. 다른 날 커피를 만든 후 누군가가 그것을 훔친 후 문을 닫는 것을 잊었습니다.

심리적 관점에서 한 번에 몇 가지 사항 만 추적 할 수 있습니다. 일반적인 추정치는 7 더하기 또는 빼기 2입니다. 수업이 여러 가지 작업을 수행하는 경우 한 번에 모든 작업을 추적해야합니다. 이렇게하면 수행중인 작업을 추적하는 기능이 줄어 듭니다. 수업이 세 가지 일을하고 그 중 하나만 원하는 경우 실제로 수업으로 무언가를하기 전에 사물을 추적하는 능력을 소진 할 수 있습니다.

여러 작업을 수행하면 코드 복잡성이 증가합니다. 가장 간단한 코드 외에도 복잡성이 증가하면 버그가 발생할 가능성이 높아집니다. 이러한 관점에서 수업은 가능한 한 단순 해지기를 원합니다.

한 가지 작업을 수행하는 클래스를 테스트하는 것이 훨씬 간단합니다. 클래스가 두 번째로 수행 한 것이 모든 테스트에서 발생했거나 발생하지 않았 음을 확인할 필요는 없습니다. 또한 고장난 조건을 수정하고 이러한 테스트 중 하나가 실패 할 때 다시 테스트하지 않아도됩니다.


2

소프트웨어가 유기적이므로 요구 사항이 지속적으로 변경되므로 가능한 한 적은 두통으로 구성 요소를 조작해야합니다. SOLID 원칙을 따르지 않으면 구체적으로 설정된 코드 기반으로 끝날 수 있습니다.

콘크리트 벽에 하중을 가하는 집을 상상해보십시오. 지지없이이 벽을 꺼내면 어떻게됩니까? 집은 아마 무너질 것입니다. 소프트웨어에서는이를 원하지 않기 때문에 많은 손상을 입히지 않고도 구성 요소를 쉽게 이동 / 교체 / 수정할 수있는 방식으로 응용 프로그램을 구성합니다.


1

나는 생각을 따른다 1 Class = 1 Job.

생리 학적 유추 사용 : 운동 (신경계), 호흡 (폐), 소화 (위), 후각 (관찰) 등. 각 제어기에는 하위 세트가 있지만 관리 여부는 1 가지입니다. 각 서브 시스템이 작동하는 방식 또는 엔드 포인트 서브 시스템인지 여부에 따라 손가락을 들어 올리거나 모낭을 키우는 것과 같은 하나의 작업 만 수행합니다.

근로자 대신 관리자 역할을한다는 사실을 혼동하지 마십시오. 일부 작업자는 한 프로세스가 자체적으로 처리하기에 너무 복잡한 작업을 수행했을 때 결국 관리자에게 승격됩니다.

내가 경험 한 것 중 가장 복잡한 부분은 언제 클래스를 운영자, 감독자 또는 관리자 프로세스로 지정할지 아는 것입니다. 어쨌든 1 책임 (운영자, 감독자 또는 관리자)에 대한 기능을 관찰하고 표시해야합니다.

클래스 / 객체가 이러한 유형 역할 중 둘 이상을 수행하면 전체 프로세스에 성능 문제 또는 프로세스 병목 현상이 발생하기 시작합니다.


대기 산소를 헤모글로빈으로 처리하는 과정을 Lung클래스 로 처리해야하더라도 인스턴스의 인스턴스 Person가 여전히 있어야 Breathe하므로 Person클래스에는 최소한 해당 방법과 관련된 책임을 위임 할 수있는 충분한 논리가 포함되어 있어야합니다. 또한 공통 소유자를 통해서만 액세스 할 수있는 상호 연결된 엔터티의 포리스트가 여러 개의 독립적 인 액세스 지점이있는 포리스트보다 추론하기가 더 쉽다고 제안합니다.
supercat

그렇습니다. Villi가 감독자 일지라도 폐는 관리자입니다. 호흡 과정은 공기 입자를 혈류로 옮기는 Worker 클래스입니다.
GoldBishop

@GoldBishop : 아마도 올바른 견해는 Person클래스가 다양한 부분의 연관성을 포함하여 엄청나게 복잡해진 "실제 인간-인간"실체와 관련된 모든 기능의 위임을 직업으로 가지고 있다고 말할 수 있습니다. . 엔티티가 500 개의 일을하는 것은 추악하지만, 그것이 실제 시스템이 모델링되는 방식이라면, 하나의 정체성을 유지하면서 500 개의 기능을 위임하는 클래스를 갖는 것이 분리 된 조각으로 모든 것을 수행하는 것보다 낫습니다.
supercat

@supercat 또는 단순히 모든 것을 구획화하고 필요한 수퍼바이저의 하위 프로세스에 대해 1 개의 클래스를 가질 수 있습니다. 그렇게하면 (이론적으로) 각 프로세스가 기본 클래스와 분리 Person되지만 성공 / 실패에 대한 체인을보고 할 수 있지만 반드시 다른 프로세스 에 영향을 미치지는 않습니다. 500 기능 (예를 들어 설정되었지만 오버 보드 및 지원할 수 없음) 필자는 이러한 유형의 기능을 파생 및 모듈 식으로 유지하려고합니다.
GoldBishop

@GoldBishop : 외부 코드가 멤버를 호출하거나 형식 자체의 메서드에 매개 변수로 전달할 수 있도록 "일시적인"형식을 선언하는 방법이 있었으면합니다. 코드 조직 관점에서 세분화 클래스는 의미가 있습니다, 심지어는 한 단계 아래로 (예 : 메소드 호출 외부 코드 갖는 Fred.vision.lookAt(George)의미가 있습니다,하지만 말에 코드를 허용 someEyes = Fred.vision; someTubes = Fred.digestion는 관계 모호하기 때문에 구역질 보인다 someEyessomeTubes.
supercat

1

특히 단일 책임 (Single Responsibility)과 같은 중요한 원칙을 통해 사람들이이 원칙을 채택하는 데는 여러 가지 이유가 있다고 개인적으로 기대합니다.

이러한 이유 중 일부는 다음과 같습니다.

  • 유지 관리-SRP를 사용하면 한 클래스에서 책임을 변경해도 다른 책임에 영향을 미치지 않으므로 유지 관리가 간단 해집니다. 각 수업에 하나의 책임 만있는 경우 한 책임에 대한 변경 사항이 다른 책임과 분리되기 때문입니다.
  • 테스트-수업에 하나의 책임이있는 경우이 책임을 테스트하는 방법을 훨씬 쉽게 파악할 수 있습니다. 클래스에 여러 책임이있는 경우 올바른 클래스를 테스트하고 클래스의 다른 책임에 의해 테스트가 영향을받지 않는지 확인해야합니다.

또한 SRP에는 SOLID 원칙 번들이 포함되어 있습니다. SRP를 준수하고 나머지를 무시하는 것은 처음에 SRP를 수행하지 않는 것만 큼 나쁩니다. 따라서 SRP 자체를 평가하지 말고 모든 SOLID 원칙의 맥락에서 평가해야합니다.


... test is not affected by other responsibilities the class has이것에 대해 자세히 설명해 주시겠습니까?
Mahdi

1

이러한 원칙의 중요성을 이해하는 가장 좋은 방법은 필요를 갖는 것입니다.

초보자 프로그래머 였을 때 디자인에 대해 많은 생각을하지 않았으며, 실제로 디자인 패턴이 존재하는지도 몰랐습니다. 내 프로그램이 성장함에 따라 한 가지를 바꾸는 것은 다른 많은 것을 바꾸는 것을 의미했습니다. 버그를 추적하기가 어려웠고 코드는 거대하고 반복적이었습니다. 객체 계층 구조는 많지 않았으며 모든 것이 가능했습니다. 새로운 것을 추가하거나 오래된 것을 제거하면 프로그램의 다른 부분에서 오류가 발생합니다. 그림을 이동.

소규모 프로젝트에서는 문제가되지 않지만 대규모 프로젝트에서는 문제가 매우 심할 수 있습니다. 나중에 디자인 패턴의 개념을 접했을 때 나는 "오 이런, 이렇게하면 일이 훨씬 쉬워 질 것"이라고 스스로에게 말했다.

당신은 정말로 필요할 때까지 디자인 패턴의 중요성을 이해할 수 없습니다. 경험을 통해 코드 유지 관리가 쉽고 코드가 강력하다고 말할 수 있기 때문에 패턴을 존중합니다.

그러나 여러분과 마찬가지로 아직 단위 테스트에 참여할 필요가 없었기 때문에 여전히 "쉬운 테스트"에 대해 확신이 없습니다.


패턴은 어떻게 든 SRP에 연결되지만 디자인 패턴을 적용 할 때 SRP는 필요하지 않습니다. 패턴을 사용하고 SRP를 완전히 무시할 수 있습니다. 비 기능적 요구 사항을 해결하기 위해 패턴을 사용하지만 프로그램에서 패턴을 사용할 필요는 없습니다. 원칙은 개발을 덜 고통스럽게 만드는 데 필수적이며 (IMO) 습관이되어야합니다.
Maciej Chałapuk

나는 당신에게 완전히 동의합니다. SRP는 어느 정도 집행자들이 객체 설계와 계층을 정리합니다. 개발자가 대상과 생각에 대해 생각하기 시작하면 개발자가 참여하는 것이 좋습니다. 개인적으로, 그것은 내 개발에도 정말 좋은 영향을 미쳤습니다.
harsimranb

1

대답은 다른 사람들이 지적했듯이 모든 것이 정확하고 모두 서로에게 피드하고 테스트하기가 쉬워 유지 관리가 쉬워지고 코드가 더 강력 해져 유지 관리가 쉬워집니다 ...

이 모든 것이 핵심 교장으로 요약됩니다. 코드는 작고 작업을 완료하기 위해 필요한만큼만 수행해야합니다. 이것은 함수만큼이나 응용 프로그램, 파일 또는 클래스에 적용됩니다. 코드가하는 일이 많을수록 이해, 유지, 확장 또는 테스트하기가 더 어렵습니다.

나는 그것이 한 단어로 요약 될 수 있다고 생각합니다 : 범위. 아티팩트의 범위에 세심한주의를 기울이십시오. 응용 프로그램의 특정 지점에서 범위가 적을수록 좋습니다.

확장 된 범위 = 복잡성 = 문제가 발생하는 더 많은 방법.


1

수업이 너무 크면 유지 관리, 테스트 및 이해하기가 어려워지며 다른 답변 이이 의지를 다루었습니다.

클래스가 문제없이 하나 이상의 책임을 가질 수는 있지만 너무 복잡한 클래스로 인해 문제가 발생합니다.

그러나“단 하나의 책임”이라는 간단한 규칙 만 있으면 새로운 수업이 필요할 때 쉽게 알 수 있습니다 .

그러나 "책임"을 정의하는 것은 어렵지만 "응용 프로그램 사양에 명시된 모든 작업을 수행"한다는 의미는 아니며 실제 기술은 문제를 작은 "책임"단위로 분류하는 방법을 알고 있습니다.

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