특정 기능을 함수로 추출해야하는 이유는 무엇입니까?


29

3 가지 작업을 수행하는 큰 방법이 있습니다. 각 방법을 별도의 기능으로 추출 할 수 있습니다. 각 작업에 대해 추가 기능을 만들면 코드가 더 나아지거나 왜 그럴까요?

분명히, 그것은 주 함수에서 적은 코드 줄을 만들 것이지만, 추가 함수 선언이있을 것이므로 클래스에는 클래스가 더 복잡해질 것이기 때문에 좋지 않다고 생각되는 추가 메소드가 있습니다.

모든 코드를 작성하기 전에해야합니까, 아니면 모든 것이 끝날 때까지 그대로두고 함수를 추출해야합니까?


19
"모든 것이 완료 될 때까지 그대로 두십시오"는 일반적으로 "완료되지 않습니다"와 동의어입니다.
Euphoric

2
그것은 일반적으로 사실이지만 YAGNI의 반대 원칙을 기억하십시오 (이 경우에는 적용되지 않습니다).
jhocking


코드 줄을 줄이는 데 너무 집중하지 마십시오. 대신 추상화 측면에서 생각하십시오. 각 기능에는 하나의 작업 만 있어야합니다. 함수가 둘 이상의 작업을 수행하는 경우 일반적으로 메소드를 리팩터링해야합니다. 이 지침을 따르면 기능이 너무 길면 거의 불가능합니다.
Adrian

답변:


35

이 책은 내가 종종 연결하는 책이지만, 여기로 다시갑니다 : Robert C. Martin의 Clean Code , 3 장, "함수".

분명히, 그것은 주 함수에서 적은 코드 줄을 만들 것이지만, 추가 함수 선언이있을 것이므로 클래스에는 클래스가 더 복잡해질 것이기 때문에 좋지 않다고 생각되는 추가 메소드가 있습니다.

+150 줄의 함수를 읽거나 3 +50 개의 줄 함수를 호출하는 함수를 선호합니까? 나는 두 번째 옵션을 선호한다고 생각합니다.

, 코드가 더 "읽기 쉬워 질 것"이라는 의미에서 더 나은 코드를 만들 것입니다. 단 하나의 기능을 수행하는 기능을 만들면 유지 관리가 쉽고 테스트 사례를 생성 할 수 있습니다.

또한, 앞서 언급 한 책에서 배운 매우 중요한 것 : 기능에 대해 정확하고 정확한 이름을 선택하십시오. 기능이 중요할수록 이름이 가장 정확해야합니다. 이름의 길이에 대해 걱정하지 마십시오.이 이름을 호출해야하는 FunctionThatDoesThisOneParticularThingOnly경우 이름을 지정하십시오.

리 팩터를 수행하기 전에 하나 이상의 테스트 케이스를 작성하십시오. 그들이 작동하는지 확인하십시오. 리팩토링이 끝나면 새 코드가 제대로 작동하는지 확인하기 위해 이러한 테스트 사례를 시작할 수 있습니다. 새로운 기능이 제대로 작동하는지 확인하기 위해 추가 "작은"테스트를 작성할 수 있습니다.

마지막으로, 이것은 내가 방금 작성한 내용과 상반되는 것이 아니며,이 리팩토링을 정말로해야하는지 스스로에게 물어보십시오 . " 리팩터링시기 "에 대한 답변을 확인하십시오. (또한 "리팩토링"에 대한 SO 질문을 검색하면 더 많은 답변을 읽을 수 있습니다)

모든 코드를 작성하기 전에해야합니까, 아니면 모든 것이 끝날 때까지 그대로두고 함수를 추출해야합니까?

코드가 이미 있고 작동하고 다음 릴리스에 대한 시간이 부족한 경우 만지지 마십시오. 그렇지 않으면 가능할 때마다 작은 기능을 수행해야하며 모든 시간이 가능할 때마다 리팩터링해야하며 모든 것이 이전과 같이 작동하는지 확인하십시오 (테스트 사례).


10
실제로 Bob Martin은 한 줄에 15 줄이있는 기능보다 2 ~ 3 줄이있는 7 가지 기능을 선호하는 것으로 여러 번 나타났습니다 ( sites.google.com/site/unclebobconsultingllc/… 참조 ). 그리고 경험 많은 개발자들이 저항하는 곳입니다. 개인적으로, 나는 그 "경험이 풍부한 개발자들"이 10 년 이상의 코딩 후에 함수로 추상화를 작성하는 것과 같은 기본적인 것을 개선 할 수 있다는 것을 받아들이는 데 어려움을 겪고 있다고 생각합니다.
Doc Brown

겸손한 견해로는 모든 소프트웨어 회사의 선반에 있어야하는 책을 참조하기위한 +1입니다.
Fabio Marcolini 2016 년

3
나는 여기서 역설을하고 있을지 모르지만, 그 책에서 거의 매일 머리에 울려 퍼지는 문구는 "각 기능은 한 가지 일만하고 잘 수행해야한다"는 것입니다. OP가 "나의 주요 기능이 세 가지 일을한다"고 말한 이후 특히 관련이있는 것 같다
wakjah

당신 말이 맞아요!
Jalayn

세 개의 개별 기능이 얼마나 얽혀 있는지에 따라 다릅니다. 서로 반복적으로 의존하는 3 개의 코드 블록보다 한 곳에있는 코드 블록을 따르는 것이 더 쉬울 수 있습니다.
user253751

13

그렇습니다. 단일 기능의 다른 "작업"을 쉽게보고 분리 할 수있는 경우.

  1. 가독성-이름이 좋은 함수는 코드를 읽을 필요없이 코드가하는 일을 명시 적으로 만듭니다.
  2. 재사용 성-필요없는 기능을 수행하는 것보다 여러 곳에서 한 가지 기능을 수행하는 것이 더 쉽습니다.
  3. 테스트 가능성-정의 된 "기능"이있는 기능과 많은 기능이있는 기능을 테스트하기가 더 쉽습니다.

그러나 이것에 문제가있을 수 있습니다 :

  • 기능을 분리하는 방법을 쉽게 알 수는 없습니다. 분리하기 전에 먼저 함수 내부의 리팩토링이 필요할 수 있습니다.
  • 함수는 거대한 내부 상태를 가지고 있습니다. 이것은 일반적으로 일종의 OOP 솔루션을 요구합니다.
  • 어떤 기능을 수행해야하는지 말하기는 어렵습니다. 그것을 단위 테스트하고 알 때까지 리팩토링하십시오.

5

당신이 포즈를 취하는 문제는 코딩, 컨벤션 또는 코딩 연습의 문제가 아니라 가독성 문제와 텍스트 편집기가 작성한 코드를 표시하는 방법입니다. 이 같은 문제가 게시물에서도 나타나고 있습니다.

긴 함수와 메소드를 다른 것으로 호출하지 않더라도 더 작은 함수로 분할해도 괜찮습니까?

기능을 하위 기능으로 분할하면 구성 될 다양한 기능을 캡슐화하려는 의도로 큰 시스템을 구현할 때 적합합니다. 그럼에도 불구하고 조만간 많은 큰 기능을 사용하게 될 것입니다. 그것들 중 일부는 읽을 수 없으며 유지하기가 어렵습니다. 하나의 긴 기능으로 유지하거나 분할하면 더 작은 기능입니다. 이는 시스템의 다른 장소에서 작업이 필요하지 않은 기능에 특히 해당됩니다. 그러한 긴 기능 중 하나를 선택하여 더 넓은 관점에서 고려하십시오.

찬성:

  • 일단 읽고 나면 함수가 수행하는 모든 opration에 대한 완전한 아이디어를 얻게됩니다 (책으로 읽을 수 있음).
  • 디버깅하려면 다른 파일 / 파일의 일부로 이동하지 않고 단계별로 실행할 수 있습니다.
  • 함수의 모든 단계에서 선언 된 변수에 자유롭게 액세스 / 사용할 수 있습니다.
  • 함수가 완전히 캡슐화 한 함수를 구현하는 알고리즘.

대조 :

  • 화면의 많은 페이지가 필요합니다.
  • 읽는 데 시간이 오래 걸립니다.
  • 다른 모든 단계를 암기하는 것은 쉽지 않습니다.

이제 긴 함수를 여러 하위 함수로 나누고 더 넓은 장래로 살펴 보도록하겠습니다.

찬성:

  • 휴가 기능을 제외하고 각 기능은 수행 된 다른 단계를 단어 (하위 기능의 이름)로 설명합니다.
  • 각 단일 기능 / 하위 기능을 읽는 데 매우 짧은 시간이 걸립니다.
  • 각 하위 기능에서 어떤 매개 변수와 변수가 영향을 받는지 분명합니다 (문제 분리).

대조 :

  • "sin ()"과 같은 함수가하는 일을 상상하기는 쉽지만 하위 함수가하는 일을 상상하기는 쉽지 않습니다.
  • 알고리즘은 이제 사라지고 이제는 하위 기능 (개요 없음)으로 배포됩니다.
  • 단계별로 디버깅 할 때, 당신이 오는 깊이 수준 함수 호출을 잊어 버리기 쉽습니다 (프로젝트 파일의 여기 저기 점프).
  • 다른 하위 기능을 읽을 때 컨텍스트를 쉽게 잃을 수 있습니다.

두 솔루션 모두 장단점이 있습니다. 실제로 최상의 솔루션은 확장, 인라인 및 전체 깊이를 위해 각 함수가 내용을 호출 할 수있는 편집기를 사용하는 것입니다. 하위 기능에서 기능을 분할하는 것이 유일한 최상의 솔루션이 될 것입니다.


2

나를 위해 코드 블록을 함수로 추출하는 네 가지 이유가 있습니다.

  • 당신은 그것을 재사용하고 있습니다 : 당신은 코드 블록을 클립 보드에 복사했습니다. 붙여 넣는 대신 함수에 넣고 블록을 양쪽의 함수 호출로 바꿉니다. 따라서 해당 코드 블록을 변경해야 할 때마다 코드를 여러 위치로 변경하는 대신 단일 기능 만 변경하면됩니다. 따라서 코드 블록을 복사 할 때마다 기능을 수행해야합니다.

  • 콜백입니다 : 이벤트 핸들러 또는 일종의 사용자 코드 라이브러리 또는 프레임 워크 호출입니다. (기능을 만들지 않고는 거의 상상할 수 없습니다.)

  • 현재 프로젝트 또는 다른 곳에서 재사용 될 것이라고 생각합니다 . 두 배열의 가장 긴 공통 하위 시퀀스를 계산하는 블록을 작성했습니다. 프로그램 이이 함수를 한 번만 호출하더라도 결국 다른 프로젝트 에서도이 함수가 필요하다고 생각합니다.

  • 자체 문서화 코드가 필요합니다 . 따라서 수행하는 작업을 요약하는 코드 블록 위에 주석 행을 작성하는 대신 전체 항목을 함수로 추출하고 주석에 작성할 대상의 이름을 지정합니다. 나는 이것을 좋아하지 않지만 사용 된 알고리즘의 이름, 알고리즘을 선택한 이유 등을 작성하기 때문에 함수 이름이 너무 길 것입니다 ...


1

변수의 범위를 가능한 한 엄격하게 지정해야한다는 충고를 들었으므로 이에 동의하기를 바랍니다. 함수는 범위의 컨테이너이고 작은 함수에서는 지역 변수의 범위가 더 작습니다. 사용 방법과시기가 훨씬 명확하고 잘못된 순서로 또는 초기화하기 전에 사용하기가 더 어렵습니다.

또한 함수는 논리적 흐름의 컨테이너입니다. 한 가지 방법 만 있으며, 그 방법은 명확하게 표시되며, 기능이 충분히 짧으면 내부 흐름이 분명해야합니다. 이것은 결함의 비율을 감소시키는 신뢰할 수있는 방법 인 순환 복잡도 를 감소시키는 효과 가있다.


0

옆으로 : 나는 달린 의 질문 에 대한 답변으로 이것을 썼지 만 (현재는 폐쇄되어 있지만) 여전히 누군가에게 도움이 될 수 있다고 생각합니다.


나는 분무 기능의 이유는 2 배이며, @jozefg가 언급 한 것처럼 사용되는 언어에 달려 있다고 생각합니다.

우려의 분리

이 작업을 수행하는 주된 이유는 서로 다른 코드 조각을 별도로 유지 하기 때문에 원하는 결과 / 기능 의도에 직접 기여 하지 않는 코드 블록은 별도의 관심사이므로 추출 할 수 있습니다.

진행률 표시 줄을 업데이트하는 백그라운드 작업이 있다고 가정하면 진행률 표시 줄 업데이트는 장기 실행 작업과 직접 관련이 없으므로 진행률 표시 줄을 사용하는 유일한 코드 인 경우에도 추출해야합니다.

JavaScript에서 getMyData () 함수가 있다고 가정하면 1) 매개 변수에서 soap 메시지를 작성하고 2) 서비스 참조를 초기화하며 3) soap 메시지로 서비스를 호출하고 4) 결과를 구문 분석하고 5) 결과를 반환합니다. 합리적인 것 같습니다.이 정확한 기능을 여러 번 작성했지만 실제로 다른 코드는 서비스에서 데이터를 가져 오는 직접적인 책임 없으므로 3 & 5 코드를 포함하여 3 개의 개인 함수로 나눌 있습니다. .

향상된 디버깅 경험

원자 기능이 완전히 있으면 스택 추적이 작업 목록이되어 성공적으로 실행 된 모든 코드를 나열합니다.

  • 내 데이터 가져 오기
    • 비누 메시지 작성
    • 서비스 참조 초기화
    • 구문 분석 된 서비스 응답-오류

데이터를 얻는 동안 오류가 있음을 발견하면 더 흥미로워 질 것입니다. 그러나 일부 도구는 세부 콜 트리를 디버깅하는 데 훨씬 유용합니다 . 예를 들어 Microsoft의 Debugger Canvas가 있습니다.

또한 하루가 끝나면 콜 트리가 그보다 더 복잡 할 때 단일 파일에서 함수 순서를 선택해야하기 때문에이 방법으로 작성된 코드를 따르기가 어려울 수 있다는 우려를 이해합니다. . 그러나 함수의 이름이 잘 지정되어 있으면 (intellisense를 사용하면 속도를 늦추지 않고 원하는 함수에 3-4 개의 camal case 단어를 사용할 수 있습니다) 파일 상단에 공용 인터페이스로 구성되어 코드가 의사 코드처럼 읽 힙니다. 코드베이스에 대한 높은 수준의 이해를 얻는 가장 쉬운 방법입니다.

참고로, 이것은 "내가 아닌 것처럼 말한 것"중 하나이며, 코드 원자를 유지하는 것이 무의미하게 IMHO와 일치하지 않는 한 의미가 없습니다.

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