개인 기능 / 메소드가 너무 많습니까?


63

잘 문서화 된 코드의 중요성을 이해합니다. 그러나 자체 문서화 코드 의 중요성도 이해합니다 . 특정 기능을 시각적으로 쉽게 읽을 수있을수록 소프트웨어 유지 관리 중에 더 빠르게 이동할 수 있습니다.

그 말로, 나는 기능을 다른 작은 기능으로 분리하고 싶습니다 . 그러나 나는 한 클래스가 하나의 공개 방법을 제공하기 위해 클래스 중 5 개 이상을 가질 수있는 시점까지 그렇게합니다. 이제 다섯 개의 개인 메소드에 다섯 개의 공개 메소드를 곱하면, 그 공개 메소드에 의해 한 번만 호출 될 약 25 개의 숨겨진 메소드를 얻게됩니다.

물론, 이제는 공개 메소드를 읽는 것이 더 쉽지만, 너무 많은 기능을 갖는 것은 나쁜 습관이라고 생각합니다.

[편집하다]

사람들은 왜 너무 많은 기능을 갖는 것이 나쁜 습관이라고 생각하는지 묻습니다.

간단한 대답 : 그것은 직감입니다.

내 생각은 소프트웨어 엔지니어링 경험에 의해 뒷받침되지는 않습니다. 저에게 "작가의 블록"을 주었지만 프로그래머에게는 불확실성입니다.

과거에는 개인 프로젝트 만 프로그래밍했습니다. 최근에 팀 기반 프로젝트로 넘어갔습니다. 이제 다른 사람들이 내 코드를 읽고 이해할 수 있도록하려고합니다.

무엇이 가독성을 향상 시킬지 잘 모르겠습니다. 한편으로는 하나의 큰 기능을 명료 한 이름을 가진 다른 작은 기능으로 분리하려고 생각했습니다. 그러나 저의 다른 측면은 그것이 단지 중복 적이라는 것입니다.

그래서 올바른 길을 고르기 위해 이것을 밝히도록 요구하고 있습니다.

[편집하다]

다음, 나는 방법의 두 가지 버전이 포함 된 내 문제를 해결합니다. 첫 번째 코드는 큰 코드 덩어리를 분리 하지 않음 으로써 문제를 해결합니다 . 두 번째 별개의 것입니다.

첫 번째 버전 :

public static int Main()
{
    // Displays the menu.
    Console.WriteLine("Pick your option");
    Console.Writeline("[1] Input and display a polynomial");
    Console.WriteLine("[2] Add two polynomials");
    Console.WriteLine("[3] Subtract two polynomials");
    Console.WriteLine("[4] Differentiate two polynomials");
    Console.WriteLine("[0] Quit");
}

두 번째 버전 :

public static int Main()
{
    DisplayMenu();
}

private static void DisplayMenu()
{
    Console.WriteLine("Pick your option");
    Console.Writeline("[1] Input and display a polynomial");
    Console.WriteLine("[2] Add two polynomials");
    Console.WriteLine("[3] Subtract two polynomials");
    Console.WriteLine("[4] Differentiate two polynomials");
    Console.WriteLine("[0] Quit");
}

위의 예에서 후자는 프로그램의 전체 런타임 동안 한 번만 사용되는 함수를 호출합니다.

참고 : 위의 코드는 일반화되었지만 내 문제와 동일합니다.

자, 여기 내 질문이 있습니다 : 어느 질문입니까? 첫 번째 또는 두 번째 중 하나를 선택합니까?


1
"너무 많은 기능을 갖는 것은 나쁜 습관입니다." 왜? 왜 이것이 나쁘게 보이는지 설명하기 위해 질문을 업데이트하십시오.
S.Lott

'클래스 응집성'이라는 개념을 읽는 것이 좋습니다. Bob Martin은이를 '클린 코드'에서 설명합니다.
JᴀʏMᴇᴇ

다른 답변에 추가하고 이름을 나누지 만 클래스가 커짐에 따라 멤버 변수가 전역 변수처럼 점점 더 커진다는 것을 명심하십시오. (리팩토링과 같은 더 나은 솔루션을 제외하고) 문제를 개선하는 한 가지 방법은 가능한 경우 (많은 상태에 액세스 할 필요가없는) 작은 개인 메소드를 독립형 함수로 분리하는 것입니다. 일부 언어는 클래스 외부 함수를 허용하고 다른 언어는 정적 클래스를 갖습니다. 이런 식으로이 기능을 독립적으로 이해할 수 있습니다.
Pablo H

"이 답변이 유용하지 않은 이유"를 아는 사람이 있다면 감사하겠습니다. 항상 배울 수 있습니다. :-)
Pablo H

@PabloH 귀하가 제공 한 답변은 OP에 대한 훌륭한 관찰과 통찰력을 제공하지만 실제로 질문에 대한 답변은 아닙니다. 주석 필드로 옮겼습니다.
maple_shaft

답변:


36

이제 다섯 개의 개인 메소드에 다섯 개의 공개 메소드를 곱하면, 그 공개 메소드에 의해 한 번만 호출 될 약 25 개의 숨겨진 메소드를 얻게됩니다.

이것이 캡슐화 (Encapsulation )로 알려져 있으며 , 이는 상위 레벨에서 제어 추상화 를 생성 합니다. 이것은 좋은 것입니다.

이 사람이 그들이에 도착하면, 코드를 읽는 것을 의미한다 startTheEngine()코드의 방법과 같은 낮은 수준의 세부 사항을 모두 무시할 수 openIgnitionModule(), turnDistributorMotor(), sendSparksToSparkPlugs(), injectFuelIntoCylinders(), activateStarterSolenoid(),과에 순서대로 실행해야합니다 다른 복잡한, 작은, 모든 기능 의 더 크고 더 추상적 인 기능을 촉진합니다 startTheEngine().

코드에서 다루고있는 문제가 해당 구성 요소 중 하나를 직접 처리하지 않는 한 코드 관리자는 샌드 박스 처리 된 캡슐화 된 기능을 무시하고 계속 진행할 수 있습니다.

또한 코드를보다 쉽게 ​​테스트 할 수 있다는 이점이 있습니다 . . 예를 들어, 테스트 케이스를 작성 turnAlternatorMotor(int revolutionRate)하고 다른 시스템과 완전히 독립적 인 기능을 테스트 할 수 있습니다. 해당 기능에 문제가 있고 출력이 예상과 다른 경우 문제가 무엇인지 알고 있습니다.

구성 요소로 분류되지 않은 코드는 테스트하기가 훨씬 어렵습니다. 갑자기 관리자는 측정 가능한 구성 요소를 파헤칠 수있는 것이 아니라 추상화 만보고 있습니다.

내 조언은 코드가 확장되고 유지 관리가 쉽고 앞으로 몇 년 동안 사용 및 업데이트 될 수 있으므로 수행중인 작업을 계속하는 것입니다.


3
따라서 한 곳에서만 호출되는 전체 클래스에 로직을 제공하는 것이 좋은 '캡슐화'라고 생각하십니까?
Steven Jeuris

9
@Steven Jeuris : 그 기능들이 비공개로되어있는 한 문제가 없습니다. 사실, 나는이 답변에 명시된 바와 같이 읽기 쉽다는 것을 알았습니다. 일련의 하위 개인 함수 (물론 적절하게 명명 된) 만 호출하는 고급 공개 함수를 이해하는 것이 훨씬 쉽습니다. 물론 모든 코드가 고급 기능 자체에 포함될 수는 있지만 코드를 한 곳에서 같은 장소에서 훨씬 더 많이 살펴보면 코드를 이해하는 데 시간이 더 걸릴 것입니다.
gablin

2
@gablin : 개인 함수는 여전히 전체 클래스 내에서 액세스 할 수 있습니다. 에 (분명히 논란의) 기사 나는 너무 많은 기능을 가지는 경우 '글로벌 가독성이'손실 방법에 대해 설명합니다. 실제로 함수가 한 가지만 수행해야한다는 것 외에 일반적인 원칙은 너무 많지 않아야한다는 것입니다. 간단한 설명과 '코드 단락'으로도 얻을 수있는 짧음으로 만 분할하여 '로컬 가독성'을 얻을 수 있습니다. 좋아, 코드는 자체 문서화되어야하지만 주석은 더 이상 사용되지 않습니다!
Steven Jeuris

1
@Steven-이해하기 쉬운 것은 무엇입니까? myString.replaceAll(pattern, originalData);또는 기본 문자열 객체를 나타내는 백업 char 배열을 포함하여 String의 기능을 사용해야 할 때마다 코드에서 전체 replaceAll 메소드를 붙여 넣습니까?
jmort253

7
@ 스티븐 제리스 : 당신은 요점이 있습니다. 클래스에 너무 많은 기능 (공개 또는 개인)이있는 경우 전체 클래스 가 너무 많은 작업을 수행하려고합니다. 이러한 경우 너무 큰 함수를 여러 개의 작은 함수로 분할하는 것처럼 여러 개의 작은 클래스로 분할하는 것이 좋습니다.
gablin

22

예, 아니오 기본 원칙은 : 방법은 오직 한 가지만해야한다는 것입니다

방법을 더 작은 방법으로 "분할"하는 것이 좋습니다. 그럼 다시, 그 방법은 최적 않을만큼 일반해야 그 "큰"방법을 제공하지만, 다른 장소에서 재사용합니다. 경우에 따라 InitializePlugins, InitializeInterface 등을 호출하는 Initialize 메서드와 같은 간단한 트리 구조를 따르는 메서드가 있습니다.

당신이 정말로 큰 메소드 / 클래스를 가지고 있다면 그것은 대개 많은 일을하고 있다는 신호이며 "블롭"을 해체하기 위해 리팩토링을해야한다. Perphaps는 추상화에서 다른 클래스의 복잡성을 숨기고 의존성 주입을 사용합니다.


1
모든 사람이 맹목적으로 '한 가지'규칙을 따르는 것은 아닙니다. ; p 좋은 대답!
Steven Jeuris

16
더 큰 방법 만 제공 하더라도 큰 방법을 더 작은 방법으로 나눕니다 . 그것을 나누는 이유는 하나의 거대한 코드 덩어리 (자체 주석이 있거나 주석 처리되지 않은)를 갖는 대신 취급하고 이해하기가 더 쉬워지기 때문입니다.
gablin

1
큰 방법을 피할 수없는 경우도 있습니다. 예를 들어, Initialize 메소드가있는 경우 InitializeSystems, InitializePlugins 등을 호출 할 수 있습니다. 리팩토링이 필요하지 않은지 항상 스스로 확인해야합니다.
Homde

1
모든 방법 (또는 기능) 의 핵심 은 명확한 개념적 인터페이스가 있어야한다는 것입니다. 그것은“계약”입니다. 당신이 그것을 고정시킬 수 없다면, 그것은 문제가 될 것이고 팩토링 잘못이 있습니다. (계약을 명시 적으로 문서화하는 것이 좋을 수도 있고, 에펠 스타일을 적용하기위한 도구를 사용할 수도 있지만, 우선 계약을하는 것만 큼 중요하지는 않습니다.)
Donal Fellows

3
@gablin : 고려해야 할 또 다른 이점 : 내가 더 작은 기능으로 나눌 때 "그들은 하나의 다른 기능 만 제공"한다고 생각하지 말고 " 지금은 다른 기능 만 제공"한다고 생각하십시오 . 더 작은 함수가 더 일반적이고 재사용 가능하기 때문에 미래에 다른 메소드를 작성할 때 큰 메소드를 리팩토링하여 많은 시간을 절약 할 수있는 경우가 여러 차례있었습니다.
GSto February

17

나는 일반적으로 너무 적은 기능보다는 너무 많은 기능을 잘못 쓰는 것이 낫다고 생각합니다. 실제로 본 규칙에 대한 두 가지 예외는 DRYYAGNI 원칙에 대한 것입니다. 거의 동일한 기능이 많은 경우 반복하지 않도록 기능을 결합하고 매개 변수를 사용해야합니다. "경우에 따라"생성하여 사용하지 않는 경우 너무 많은 기능을 가질 수도 있습니다. 가독성과 유지 관리 성을 추가하면 한 번만 사용되는 기능을 갖는 데 아무런 문제가 없습니다.


10

jmort253의 훌륭한 답변은 "그렇지만"답변으로 이어졌습니다.

작은 개인용 메소드를 많이 사용하는 것은 나쁘지 않지만 여기에 질문을 올리는 끔찍한 느낌에주의하십시오. :) 개인 메소드에만 초점을 맞춘 테스트를 작성하는 시점에 도달하면 (직접 호출하거나 특정 방식으로 호출되도록 시나리오를 설정하여 결과를 테스트 할 수 있음) 물러서야합니다

당신이 가질 수있는 것은 탈출하려고하는 자체 초점이 맞는 공개 인터페이스가있는 새로운 클래스입니다. 이 시점에서 현재 개인 메소드에 존재하는 기존 클래스 기능의 일부를 캡슐화하기 위해 클래스를 추출하는 방법에 대해 생각할 수 있습니다. 이제 원래 클래스는 새 클래스를 종속성으로 사용할 수 있으며 해당 클래스에 대해 더 집중된 테스트를 직접 작성할 수 있습니다.


나는 이것이 훌륭한 대답이라고 생각하고 코드가 반드시 갈 수있는 방향이라고 생각합니다. 모범 사례에서는 필요한 기능을 제공하는 가장 제한적인 액세스 한정자를 사용한다고 말합니다. 클래스 외부에서 메소드를 사용하지 않는 경우 private이 가장 적합합니다. 메소드를 공용으로 만들거나 다른 클래스로 이동하여 다른 곳에서 사용할 수 있도록하는 것이 더 합리적이라면 다른 방법으로도 사용할 수 있습니다. +1
jmort253

8

내가 참여한 거의 모든 OO 프로젝트는 너무 큰 클래스와 너무 긴 메소드로 인해 심하게 고통 받았다.

다음 코드 섹션을 설명하는 주석이 필요하다고 생각되면 해당 섹션을 별도의 방법으로 추출합니다. 장기적으로 이것은 코드를 더 짧게 만듭니다. 이 작은 방법들은 처음에 예상했던 것보다 더 많이 재사용됩니다. 당신은 그들 중 많은 무리를 별도의 클래스로 모을 기회를보기 시작합니다. 곧 당신은 당신의 문제에 대해 더 높은 수준에서 생각하고 있으며, 모든 노력은 훨씬 더 빠릅니다. 작은 방법으로 만든 작은 클래스를 만들 수 있기 때문에 새로운 기능을보다 쉽게 ​​작성할 수 있습니다.


7

5 개의 공개 메소드와 25 개의 개인 메소드가있는 클래스는 그렇게 크지 않습니다. 클래스에 명확한 책임이 있고 메소드 수에 대해 너무 걱정하지 마십시오.

즉, 이러한 개인용 메소드는 전체 작업의 특정 측면에만 초점을 두어야하지만 "doSomethingPart1", "doSomethingPart2"방식이 아닙니다. 이러한 개인적인 방법이 임의로 분리 된 긴 이야기의 일부인 경우에는 아무것도 얻지 못합니다.


+1 "그 개인적인 방법이 임의로 분리 된 긴 이야기의 일부라면, 각각 문맥 전체에서 의미가없는 것이라면 아무것도 얻지 못합니다."
Mahdi

4

R. Martin의 Clean Code (p. 176) 에서 발췌 :

중복, 코드 표현 및 SRP를 제거하는 것과 같은 기본 개념조차도 너무 많이 취할 수 있습니다. 클래스와 메서드를 작게 만들기 위해 너무 작은 클래스와 메서드를 만들 수 있습니다. 따라서이 규칙 [클래스 및 메소드 수를 최소화]은 함수 및 클래스 수를 낮게 유지함을 제안합니다. 높은 계급과 방법 수는 때때로 무의미한 교리의 결과입니다.


3

일부 옵션 :

  1. 괜찮아. 비공개 메소드의 이름을 지정하고 공개 메소드의 이름을 지정하십시오. 공개 방법의 이름을 잘 지정하십시오.

  2. 이 헬퍼 메소드 중 일부를 헬퍼 클래스 또는 헬퍼 모듈로 추상화하십시오. 그리고이 헬퍼 클래스 / 모듈의 이름이 잘 지정되어 있고 그 자체가 확실한 추상화인지 확인하십시오.


1

"너무 많은 개인 메소드"라는 문제가 있다고 생각하지 않는다면 코드 아키텍처가 열악한 증상 일 것입니다.

여기 내가 어떻게 생각하는지 ... 아키텍처가 잘 설계 되었다면, 일반적으로 X를 수행하는 코드의 위치는 분명합니다. 이에 대한 몇 가지 예외가있는 경우 허용됩니다. 그러나 이러한 예외를 표준으로 처리하려면 아키텍처를 리팩터링해야합니다.

문제가 클래스를 열 때 큰 덩어리를 작은 코드 덩어리로 나누는 개인 메소드로 가득 차 있다면 아마도 아키텍처가 누락 된 증상 일 것입니다. 클래스와 아키텍처 대신이 코드 영역은 하나의 클래스 내에서 절차적인 방식으로 구현되었습니다.

여기서 균형을 찾는 것은 프로젝트와 요구 사항에 따라 다릅니다. 이에 대한 반론은 하급 프로그래머가 설계자 50 클래스를 취하는 50 줄의 코드로 솔루션을 사용할 수 있다는 것입니다.


0

개인 기능 / 메소드가 너무 많습니까?

예.

파이썬에서는 "private"이라는 개념 (C ++, Java 및 C #에서 사용)이 실제로 존재하지 않습니다.

"비공개 정렬"명명 규칙이 있지만 그게 전부입니다.

비공개는 테스트하기 어려운 코드로 이어질 수 있습니다. 또한 코드를 간단하게 닫아서 "폐쇄 원칙"을 위반할 수도 있습니다.

결과적으로 파이썬을 사용한 사람들에게는 개인 함수는 가치가 없습니다. 모든 것을 공개하고 완료하십시오.


1
공개 폐쇄 원칙을 어기려면 어떻게해야합니까? 공용 메소드가 더 작은 개인 메소드로 분할되었는지 여부에 관계없이 코드는 확장을 위해 열려 있고 수정을 위해 닫힙니다.
byxor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.