한 번만 호출되는 한 줄 함수


120

한 줄의 코드를 수행하고 프로그램에서 한 번만 호출 되는 매개 변수가없는 ( 편집 : 반드시 그런 것은 아님) 함수를 고려하십시오 (차후에 다시 필요하지는 않지만).

쿼리를 수행하고, 값을 확인하고, 정규 표현식과 관련된 작업을 수행 할 수 있습니다.

이에 대한 근거는 읽기 어려운 평가를 피하는 것입니다.

if (getCondition()) {
    // do stuff
}

getCondition()한 줄 함수는 어디에 있습니까 ?

내 질문은 간단합니다 : 이것은 좋은 습관입니까? 괜찮아 보이지만 장기에 대해서는 모른다 ...


9
코드 조각이 암시 적 리시버 (예 : 이것)를 가진 OOP 언어에서 온 것이 아니라면 getCondition ()은 아마도 전역 상태에 의존하기 때문에 이것은 나쁜 습관 일 것입니다.
Ingo

2
그는 getCondition ()에 인수가있을 수 있음을 암시했을 수 있습니다.
user606723

24
@Ingo-일부는 실제로 글로벌 상태입니다. 현재 시간, 호스트 이름, 포트 번호 등은 모두 "글로벌"입니다. 디자인 오류는 본질적으로 글로벌이 아닌 것을 글로벌로 만듭니다.
James Anderson

1
왜 그냥 인라인하지 getCondition않습니까? 당신이 말하는 것처럼 작고 드물게 사용된다면 이름을 짓는 것은 아무것도 성취하지 못합니다.
davidk01

12
davidk01 : 코드 가독성.
wjl

답변:


240

한 줄에 따라 다릅니다. 행이 읽기 쉽고 간결한 경우 기능이 필요하지 않을 수 있습니다. 간단한 예 :

void printNewLine() {
  System.out.println();
}

OTOH, 함수가 복잡한, 읽기 어려운 표현을 포함하는 코드 줄에 좋은 이름을 주면 완벽하게 정당화됩니다 (나에게). 고안된 예 (여기에서 가독성을 위해 여러 줄로 나)) :

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}

99
+1. 여기서 마법의 단어는 "자체 문서화 코드"입니다.
Konamiman

8
Bob 아저씨가 "조건부 캡슐화"라고 부르는 것에 대한 좋은 예입니다.
Anthony Pegram

12
@Aditya : taxPayer이 시나리오에서 전 세계적으로 말하는 것은 없습니다 . 아마도이 클래스는 속성 TaxReturn이고 taxPayer속성입니다.
Adam Robinson

2
또한 예를 들어 javadoc에서 선택하여 공개적으로 볼 수있는 방식으로 함수를 문서화 할 수 있습니다.

2
Bob Martin의 Clean Code @dallin은 많은 실제 코드 예제를 보여줍니다. 단일 수업에 너무 많은 기능이 있다면 수업이 너무 클까요?
Péter Török

67

예, 모범 사례를 충족시키는 데 사용할 수 있습니다. 예를 들어, 분명한 이름의 함수가 더 큰 함수 내에 해당 코드 행을 가지고 있고 그 기능을 설명하는 한 줄 주석이 필요한 것보다 한 줄만 있어도 일부 작업을 수행하는 것이 좋습니다. 또한 인접한 코드 줄은 동일한 추상화 수준에서 작업을 수행해야합니다. 반례는 다음과 같습니다.

startIgnition();
petrolFlag |= 0x006A;
engageChoke();

이 경우 중간 줄을 의미있는 이름의 함수로 옮기는 것이 좋습니다.


15
0x006A는 사랑 스러우며 : 첨가제 a, b 및 c를 가진 탄화 된 연료의 DA 잘 알려진 상수.
코더

2
+1 나는 자기 문서화 코드를 위해 모두 :) 나는 블록을 통해 일정한 추상화 수준을 유지하는 데 동의한다고 생각하지만 그 이유를 설명 할 수는 없습니다. 확장 하시겠습니까?
vemv

3
방금 그 말을하는 경우 petrolFlag |= 0x006A;의사 결정의 어떤 종류없이, 단순히 말을 더 나은 것 petrolFlag |= A_B_C; 없이 추가 기능. 아마도 특정 기준을 충족하고 '여기에 기능이 필요합니다.'라고 분명히 말한 engageChoke()경우에만 호출 petrolFlag해야합니다. 단지 작은 작은 니트,이 답변은 기본적으로 그렇지 않습니다 :)
Tim Post

9
메소드 내에서 해당 코드를 지적하기위한 +1은 동일한 추상화 레벨에 있어야합니다. @vemv, 코드를 읽는 동안 다른 추상화 수준 사이에서 위아래로 이동할 필요가 없으므로 코드를 이해하기 쉽기 때문에 좋은 습관입니다. 추상화 레벨 스위치를 메소드 호출 / 반환 (예 : 구조적 "점프")에 연결하는 것은 코드를보다 유연하고 깨끗하게 만드는 좋은 방법입니다.
Péter Török

21
아니요, 완전히 구성 된 가치입니다. 그러나 당신이 그것에 대해 궁금해한다는 사실은 요점을 강조합니다. 코드가 말했다면 mixLeadedGasoline(), 당신은 할 필요가 없습니다!
Kilian Foth

37

많은 경우에 그러한 함수는 좋은 스타일이라고 생각하지만 다른 장소 에서이 조건을 사용할 필요가없는 경우 로컬 부울 변수를 대안으로 고려할 수 있습니다.

bool someConditionSatisfied = [complex expression];

이것은 코드 리더에 대한 힌트를 제공하고 새로운 기능을 도입하지 않아도됩니다.


1
부울은 조건 함수 이름이 어렵거나 오도 될 수있는 경우에 특히 유용합니다 (예 :) IsEngineReadyUnlessItIsOffOrBusyOrOutOfService.
dbkk

2
나는이 충고가 나쁜 생각을 겪었다 : 부울 상태는 기능의 핵심 사업에서주의를 산만하게한다. 또한 지역 변수를 선호하는 스타일은 리팩토링을 어렵게 만듭니다.
Wolf

1
@ Wolf OTOH, 코드의 "추상 깊이"를 줄이기 위해 이것을 함수 호출보다 선호합니다. IMO, 함수로 점프하는 것은 명시적이고 직접적인 몇 줄의 코드보다 큰 컨텍스트 전환입니다. 특히 부울을 반환하는 경우 특히 그렇습니다.
Kache

@Kache OOP의 경우 멤버 함수를 사용하면 디자인을 훨씬 유연하게 유지하는 것이 객체 지향 코드인지 여부에 달려 있다고 생각합니다. 그것은 실제로 상황에 달려 있습니다 ...
Wolf

23

Peter의 답변 외에도 미래의 특정 시점에서 해당 조건을 업데이트해야 할 경우 단일 편집 점 만 가질 것을 제안하는 방식으로 캡슐화하여 조건을 업데이트해야합니다.

베드로의 예에 따르면

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}

이된다

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isMutant() 
        && (taxPayer.getNumberOfThumbs() > 2 
        || (taxPayer.getAge() > 123 && taxPayer.getEmployer().isXMan()));
}

한 번만 편집하면 전체적으로 업데이트됩니다. 현명한 유지 관리 능력은 플러스입니다.

성능면에서 가장 최적화 된 컴파일러는 함수 호출을 제거하고 어쨌든 작은 코드 블록을 인라인합니다. 이와 같은 것을 최적화하면 실제로 함수 크기, 반환 등에 필요한 명령어를 삭제하여 블록 크기를 줄일 수 있으므로 인라인을 막을 수있는 상황에서도 일반적으로 안전합니다.


2
단일 편집 점을 유지하면 얻을 수있는 이점을 이미 알고 있습니다. 따라서 "... 한 번만 호출"이라는 질문이 표시됩니다. 그럼에도 불구하고 컴파일러 최적화에 대해 아는 것이 좋지만, 항상 이런 종류의 명령어를 그대로 따르는 것으로 생각했습니다.
vemv

간단한 조건을 테스트하기 위해 방법을 분리하면 방법을 변경하여 조건을 변경할 수 있음을 의미합니다. 그러한 의미는 사실 일 때 유용하지만 그렇지 않을 때는 위험 할 수 있습니다. 예를 들어, 코드가 사물을 가벼운 물체, 짙은 녹색 물체 및 무 녹색의 물체로 나눌 필요가 있다고 가정하십시오. 객체에는 빠른 "seemsGreen"속성이있어 무거운 객체에는 신뢰할 수 있지만 가벼운 객체에서는 오 탐지를 반환 할 수 있습니다. 또한 "measureSpectralResponse"함수는 느리지 만 모든 객체에 대해 안정적으로 작동합니다.
supercat

seemsGreen경량 객체가 녹색인지 아닌지 코드가 신경 쓰지 않으면 경량 객체에 신뢰할 수 없다는 사실은 관련이 없습니다. 그러나 "경량"의 정의가 변경되어 true를 반환하는 녹색이 아닌 일부 객체가 seemsGreen"경량"으로보고되지 않으면 "경량"의 정의를 변경하면 객체를 테스트하는 코드가 손상 될 수 있습니다. "녹색"입니다. 경우에 따라 코드에서 녹색과 가중치를 함께 테스트하면 테스트가 별도의 방법 인 경우보다 테스트 간의 관계가 더 명확해질 수 있습니다.
supercat

5

가독성 (또는 그것을 보완하는 것)에 추가하여 적절한 추상화 레벨에서 함수를 작성할 수 있습니다.


1
난 당신이 무슨 뜻인지 이해가 안 돼요 ...
vemv

1
@vemv : 나는 "적절한 추상화 수준"을 의미한다고 생각합니다. 다른 추상화 수준이 혼합 된 코드 (예 : Kilian Foth가 이미 말한 것)로 끝나지 않습니다. 주변 코드가 현재 상황에 대해 더 높은 수준의 관점 (예 : 엔진 구동)을 고려할 때 코드가 중요하지 않은 세부 사항 (예 : 연료의 모든 결합 형성)을 처리하는 것을 원하지 않습니다. 전자는 후자를 어수선하게 만들고 언제든지 모든 수준의 추상화를 고려해야하기 때문에 코드를 읽고 유지하기가 쉽지 않습니다.
Egon

4

따라 다릅니다. 때로는 한 줄인 경우에도 함수 / 메서드에 식을 캡슐화하는 것이 좋습니다. 읽기가 복잡하거나 여러 곳에서 필요한 경우 좋은 방법으로 간주합니다. 단일 변경 지점과 더 나은 가독성을 도입 했으므로 장기적으로 유지 관리가 더 쉽습니다 .

그러나 때로는 필요하지 않은 것일 수도 있습니다. 어쨌든 표현이 읽기 쉽고 / 또는 한 곳에 나타나면 줄 바꿈하지 마십시오.


3

나는 당신이 그들 중 몇 가지를 가지고 있다면 괜찮다고 생각하지만 코드에 많은 것이있을 때 문제가 발생합니다. 그리고 컴파일러가 실행될 때 또는 사용하는 언어에 따라 인터 피터가 실행될 때 메모리에서 해당 기능을 수행합니다. 그래서 당신이 그들 중 3 명이라고 말하면 컴퓨터가 알 수 있다고 생각하지 않지만 100의 작은 것들을 시작하기 시작하면 시스템은 한 번만 호출 된 다음 파괴되지 않는 메모리에 함수를 등록해야합니다.


Stephen의 답변에 따르면, 이것이 항상 일어날 수있는 것은 아닙니다 (컴파일러의 마법을 맹목적으로 신뢰하는 것은 어쨌든 좋지는 않습니다)
vemv

1
예, 많은 사실에 따라 해결되어야합니다. 인터 페인트 언어 인 경우, 캐시를 위해 무언가를 설치하지 않으면 매번 한 줄 기능으로 인해 시스템이 작동하지 않을 수 있습니다. 이제 컴파일러에 관해서는 약한 날과 컴파일러의 친구가되어 친구가되고 작은 혼란을 없애거나 실제로 생각할 필요가 있는지에 대한 계획의 시작에 중요합니다. 정확한 양을 알고 있다면 루프를 반복하여 여러 번 복사하여 붙여 넣을 때 루프가 항상 몇 배 더 잘 실행됩니다.
WojonsTech

행성의 정렬에 +1 :) 그러나 마지막 문장은 완전히 나에게 소리가 들립니다. 정말로 그렇게합니까?
vemv

그것은 그것이 대부분의 시간에 달려 있습니다. 아니면 속도 상승과 그와 같은 다른 것들을 확인하기 위해 올바른 금액의 지불을받지 않으면 안됩니다. 그러나 오래된 컴파일러에서는 복사하여 붙여 넣은 다음 컴파일러가 9/10을 알아 내도록하는 것이 좋습니다.
WojonsTech

3

코드 리산 주석의 실제 의미를 명시하기 위해 리팩토링 한 응용 프로그램에서 최근 에이 정확한 작업을 수행했습니다.

protected void PaymentButton_Click(object sender, EventArgs e)
    Func<bool> HaveError = () => lblCreditCardError.Text == string.Empty && lblDisclaimer.Text == string.Empty;

    CheckInputs();

    if(HaveError())
        return;

    ...
}

궁금한 점은 무엇입니까?
vemv

5
@ vemv : 나에게 C #처럼 보입니다.
Scott Mitchell

또한 주석보다 추가 식별자를 선호합니다. 그러나 이것은 짧게 유지하기 위해 지역 변수도입하는 것과 실제로 다른 if가요? 이 로컬 (람다) 접근 방식은 PaymentButton_Click함수 전체를 읽기 어렵게 만듭니다. lblCreditCardError당신의 예제는 구성원이 될 것 같다, 그래서 또한 HaveError개체에 대한 유효한 (개인) 술어이다. 나는 이것을 하향 조정하는 경향이 있지만, 나는 C # 프로그래머가 아니기 때문에 저항한다.
Wolf

@ 울프 이봐, 그래. 나는 이것을 꽤 오래 전에 썼다. :) 나는 분명히 요즘 일을 다르게한다. 오류가 나에게 왜 그냥에서 부울을 반환하지 않았습니다 ... 싫증이 나다하게이 있다면 사실, 라벨의 내용을 보면 볼 CheckInputs()???
joshperry

0

한 줄을 이름이 지정된 메소드로 옮기면 코드를 더 쉽게 읽을 수 있습니다. 많은 사람들이 이미 그 자체를 언급했습니다 ( "자체 문서화 코드"). 메소드로 이동하는 다른 장점은 단위 테스트가 더 쉽다는 것입니다. 자체 메소드로 분리되고 단위 테스트를 수행하면 버그가 발견되면이 메소드에 없는지 확인할 수 있습니다.


0

이미 좋은 답변이 많이 있지만 언급할만한 특별한 경우가 있습니다 .

한 줄 문장 에 주석이 필요하고 그 목적을 명확하게 식별 할 수 있다면 (이름을 의미) API 주석으로 주석을 강화하면서 함수 추출을 고려하십시오. 이렇게하면 함수 호출을보다 쉽고 빠르게 이해할 수 있습니다.

흥미롭게도, 현재 할 일이 없다면 같은 작업을 수행 할 수 있지만 확장이 필요하다는 의견 (아주 가까운 미래 1) )

def sophisticatedHello():
    # todo set up
    say("hello")
    # todo tear down

뿐만 아니라 이것으로 바뀔 수있었습니다

def sophisticatedHello():
    setUp()
    say("hello")
    tearDown()

1) 당신은 이것에 대해 정말로 확신해야합니다 ( YAGNI 원칙 참조 )


0

언어가 지원하는 경우 일반적으로 레이블이 지정된 익명 함수를 사용하여이를 수행합니다.

someCondition = lambda p: True if [complicated expression involving p] else False
#I explicitly write the function with a ternary to make it clear this is a a predicate
if (someCondition(p)):
    #do stuff...

IMHO 이것은 if레이블이 작은 전역 / 패키지 네임 스페이스를 어지럽히 지 않으면 서 복잡한식이 조건을 어지럽히 지 않는 가독성 이점을 제공하기 때문에 좋은 절충안 입니다. "정의"기능이 사용되는 곳에서 정의를 쉽게 수정하고 읽을 수 있도록하는 이점이 있습니다.

술어 함수일 필요는 없습니다. 나는 반복적 인 보일러 플레이트를 이와 같은 작은 기능으로 묶고 싶습니다. (괄호 구문을 어지럽히 지 않고 파이썬 목록 생성에 특히 효과적입니다). 예를 들어, 파이썬에서 PIL로 작업 할 때 다음과 같이 단순화 된 예

#goal - I have a list of PIL Image objects and I want them all as grayscale (uint8) numpy arrays
im_2_arr = lambda im: array(im.convert('L')) 
arr_list = [im_2_arr(image) for image in image_list]

"lambda p : [p와 관련된 복잡한 표현]"대신 [p와 관련된 복잡한 표현] else False 인 경우 "lambda p : True" 8-)
Hans-Peter Störr

@hstoerr는 그 줄 바로 아래의 주석에 있습니다. 나는 우리가 someCondition이 술어임을 명시 적으로 알리고 싶습니다. 엄청나게 불필요하지만 코드를 많이 쓰지 않는 사람들이 읽은 많은 과학적 대본을 쓰지만, 개인적으로 동료들이 [] == False다른 것을 알지 못하기 때문에 혼란스러워하기보다는 여분의 간결함을 갖는 것이 더 적절하다고 생각합니다 '항상'직관적이지 않은 유사한 파이 토닉 동등성. 기본적으로 someCondition이 술어임을 표시하는 방법입니다.
crasic

그냥 내 명백한 실수를 취소 할 수 [] != False있지만, []부울로 캐스팅 할 때와 같은 거짓이다
crasic

@crasic : 독자들이 []가 False로 평가된다는 것을 알기를 기대하지 않을 때, 독자가 []가 False로 평가된다는 것을 여전히 알도록 요구하는 len([complicated expression producing a list]) == 0것보다는을 사용 True if [blah] else False하는 것을 선호 합니다.
Lie Ryan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.