한 번만 호출되는 한 줄 함수


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.