내가 사용하는 몇 가지 함수에는 6 개 이상의 매개 변수가 있지만 대부분의 라이브러리에서는 3 개 이상의 함수를 찾는 것이 거의 없습니다.
이러한 많은 추가 매개 변수는 종종 함수 동작을 변경하기위한 이진 옵션입니다. 나는이 매개 변수가없는 함수 중 일부는 리팩토링되어야한다고 생각합니다. 너무 많은 숫자에 대한 지침이 있습니까?
내가 사용하는 몇 가지 함수에는 6 개 이상의 매개 변수가 있지만 대부분의 라이브러리에서는 3 개 이상의 함수를 찾는 것이 거의 없습니다.
이러한 많은 추가 매개 변수는 종종 함수 동작을 변경하기위한 이진 옵션입니다. 나는이 매개 변수가없는 함수 중 일부는 리팩토링되어야한다고 생각합니다. 너무 많은 숫자에 대한 지침이 있습니까?
답변:
가이드 라인을 본 적이 없지만 경험상 3 ~ 4 개 이상의 매개 변수를 사용하는 함수는 다음 두 가지 문제 중 하나를 나타냅니다.
더 많은 정보 없이는보고있는 내용을 말하기가 어렵습니다. 리팩토링은 현재 함수에 전달되는 플래그에 따라 함수를 더 작은 함수로 분할하는 것입니다.
이 작업을 수행하면 좋은 이점이 있습니다.
if
으로 하나의 메소드에서 모두 수행하는 구조보다 설명적인 이름을 가진 많은 메소드를 호출 하는 구조 로 구성된 "규칙 목록"을 읽는 것이 훨씬 쉽다 는 것을 알게되었습니다."Clean Code : Agile Software Craftsmanship 핸드북"에 따르면, 0은 이상적이며, 1 ~ 2 개는 허용되며, 특별한 경우 3 개, 4 개 이상은 절대 허용되지 않습니다!
저자의 말 :
함수에 대한 이상적인 인수 수는 0입니다 (나일론). 다음은 하나 (모나 딕)가오고 그 뒤에는 2 (이차)가옵니다. 가능한 경우 세 가지 주장 (삼국 법)을 피해야합니다. 3 개 이상 (다 극적)은 매우 특별한 타당성을 요구하므로 어쨌든 사용해서는 안됩니다.
이 책에는 매개 변수가 큰 기능에 대해서만 이야기하는 장이 있으므로이 책은 필요한 매개 변수의 양을 잘 설명 할 수 있다고 생각합니다.
내 개인적인 견해로는 한 가지 매개 변수가 아무도 아닌 것보다 낫습니다.
예를 들어, 내 의견으로는 두 번째 선택이 더 낫습니다. 왜냐하면 메소드가 처리하는 것이 더 명확하기 때문입니다.
LangDetector detector = new LangDetector(someText);
//lots of lines
String language = detector.detectLanguage();
vs.
LangDetector detector = new LangDetector();
//lots of lines
String language = detector.detectLanguage(someText);
많은 매개 변수에 대해 이것은 일부 변수가 단일 객체로 그룹화 될 수 있다는 표시 일 수 있습니다.이 경우 많은 부울은 함수 / 메소드가 하나 이상의 작업을 수행하고 있음을 나타낼 수 있습니다. 이러한 기능을 각각 다른 기능으로 리팩토링하는 것이 좋습니다.
String language = detectLanguage(someText);
. 두 경우 모두 정확히 같은 수의 인수를 전달하면 언어가 좋지 않기 때문에 함수 실행을 두 개로 나눕니다.
Matrix.Create(input);
어디에 있습니까? 이렇게하면 9 대신 10 개의 요소를 포함하는 행렬을 만들 때 별도의 과부하가 필요하지 않습니다.input
IEnumerable<SomeAppropriateType>
응용 프로그램의 도메인 클래스가 올바르게 설계된 경우 클래스가 작업을 수행하는 방법을 알고 작업을 수행하기에 충분한 데이터가 있기 때문에 함수에 전달하는 매개 변수의 수가 자동으로 줄어 듭니다.
예를 들어, 3 학년 학급에 과제를 완료하도록 요청하는 관리자 클래스가 있다고 가정합니다.
올바르게 모델링하면
3rdGradeClass.finishHomework(int lessonId) {
result = students.assignHomework(lessonId, dueDate);
teacher.verifyHomeWork(result);
}
이것은 간단합니다.
올바른 모델이 없으면 방법은 다음과 같습니다.
Manager.finishHomework(grade, students, lessonId, teacher, ...) {
// This is not good.
}
올바른 모델은 올바른 함수가 자신의 클래스에 위임되고 (단일 책임) 작업을 수행하기에 충분한 데이터가 있기 때문에 항상 메서드 호출 사이의 함수 매개 변수를 줄입니다.
매개 변수 수가 증가 할 때마다 모델을 검사하여 응용 프로그램 모델을 올바르게 설계했는지 확인합니다.
전송 객체 또는 구성 객체를 생성해야 할 경우, 큰 구성 객체를 생성하기 전에 먼저 빌더 패턴을 사용하여 작은 빌드 된 객체를 생성합니다.
다른 답변이 취하지 않는 한 가지 측면은 성능입니다.
충분히 낮은 수준의 언어 (C, C ++, 어셈블리)로 프로그래밍하는 경우, 특히 많은 수의 함수가 호출되는 경우 많은 아키텍처의 매개 변수가 일부 아키텍처에서 성능을 저하시킬 수 있습니다.
예를 들어 ARM에서 함수를 호출하면 처음 4 개의 인수가 레지스터 r0
에 배치 r3
되고 나머지 인수는 스택으로 푸시되어야합니다. 인수 수를 5 미만으로 유지하면 중요한 기능에 상당한 차이가 생길 수 있습니다.
매우 자주 호출되는 함수, 프로그램이 호출 할 때마다 성능에 영향을 줄 수 있습니다 (전에 인수를 설정해야한다는 심지어는 사실 r0
에 r3
호출 된 함수에 의해 덮어 쓸 수 있습니다 및 다음 호출하기 전에 교체되어야 할 것이다) 그래서 그런 측면에서 인수가 0이 가장 좋습니다.
최신 정보:
KjMag는 흥미로운 인라인 주제를 제시합니다. 인라이닝은 컴파일러가 순수한 어셈블리로 작성하는 경우 수행 할 수있는 것과 동일한 최적화를 수행 할 수 있도록하기 때문에이를 완화시킵니다. 다시 말해, 컴파일러는 호출 된 함수가 사용하는 매개 변수와 변수를 확인할 수 있으며 스택 읽기 / 쓰기가 최소화되도록 레지스터 사용을 최적화 할 수 있습니다.
그래도 인라인에 문제가 있습니다.
inline
모든 경우에 필수적으로 취급 된다면 이진 성장은 폭발 할 것이다 .inline
하거나 실제로 발생할 때 오류를 발생시키는 컴파일러가 많이 있습니다.inline
.)
매개 변수 목록이 5 개 이상으로 증가하면 "컨텍스트"구조 또는 오브젝트 정의를 고려하십시오.
이것은 기본적으로 합리적인 기본값이 설정된 모든 선택적 매개 변수를 보유하는 구조입니다.
C 절차 세계에서는 일반 구조가 수행됩니다. Java, C ++에서는 간단한 객체로 충분합니다. 객체의 유일한 목적은 "공개"로 설정 가능한 값을 보유하는 것이므로 게터 나 세터를 망설이지 마십시오.
아니요, 표준 가이드 라인이 없습니다.
그러나 많은 매개 변수가있는 기능을 더 견딜 수있게하는 몇 가지 기술이 있습니다.
인수 목록 매개 변수 (args *) 또는 사전 인수 매개 변수 (kwargs **
)를 사용할 수 있습니다.
예를 들어, 파이썬에서 :
// Example definition
def example_function(normalParam, args*, kwargs**):
for i in args:
print 'args' + i + ': ' + args[i]
for key in kwargs:
print 'keyword: %s: %s' % (key, kwargs[key])
somevar = kwargs.get('somevar','found')
missingvar = kwargs.get('somevar','missing')
print somevar
print missingvar
// Example usage
example_function('normal parameter', 'args1', args2,
somevar='value', missingvar='novalue')
출력 :
args1
args2
somevar:value
someothervar:novalue
value
missing
또는 객체 리터럴 정의 구문을 사용할 수 있습니다
예를 들어 AJAX GET 요청을 시작하기위한 JavaScript jQuery 호출은 다음과 같습니다.
$.ajax({
type: 'GET',
url: 'http://someurl.com/feed',
data: data,
success: success(),
error: error(),
complete: complete(),
dataType: 'jsonp'
});
jQuery의 ajax 클래스를 살펴보면 설정할 수있는 속성이 훨씬 더 많다 (약 30 개). 대부분 아약스 통신은 매우 복잡하기 때문입니다. 다행히도 객체 리터럴 구문은 삶을 쉽게 만듭니다.
C # intellisense는 매개 변수에 대한 능동적 인 문서를 제공하므로 오버로드 된 메서드의 매우 복잡한 배열을 보는 것은 드문 일이 아닙니다.
python / javascript와 같이 동적으로 유형이 지정된 언어에는 그러한 기능이 없으므로 키워드 인수와 객체 리터럴 정의를 보는 것이 훨씬 일반적입니다.
객체가 인스턴스화 될 때 설정되는 속성을 명시 적으로 볼 수 있기 때문에 복잡한 메소드를 관리하기 위해 객체 리터럴 정의 ( C #에서도 )를 선호합니다 . 기본 인수를 처리하려면 약간 더 많은 작업을 수행해야하지만 장기적으로 코드를 훨씬 더 읽기 쉽게 할 수 있습니다. 객체 리터럴 정의를 사용하면 문서에 대한 의존성을 깨뜨려 코드가 한 눈에 무엇을하고 있는지 이해할 수 있습니다.
IMHO, 오버로드 된 메소드는 과대 평가되었습니다.
참고 : 올바른 읽기 전용 액세스 제어를 기억하면 C #의 객체 리터럴 생성자에 대해 작동해야합니다. 본질적으로 생성자에서 속성을 설정하는 것과 동일하게 작동합니다.
사소하지 않은 코드를 동적으로 유형이 지정된 (python) 및 / 또는 기능 / 시제품 javaScript 기반 언어로 작성하지 않은 경우 직접 사용해 보는 것이 좋습니다. 깨달은 경험이 될 수 있습니다.
함수 / 메소드 초기화에 대한 포괄적 인 접근 방식에 대한 매개 변수에 의존하는 것이 먼저 무섭지 않을 수 있지만 불필요한 복잡성을 추가하지 않고도 코드로 훨씬 더 많은 것을 배우게됩니다.
최신 정보:
정적으로 형식이 지정된 언어에서 사용하는 방법을 보여주는 예제를 제공했을 수도 있지만 현재는 정적으로 형식화 된 컨텍스트에서 생각하지 않습니다. 기본적으로 동적으로 입력 된 컨텍스트에서 갑자기 다시 전환하기 위해 너무 많은 작업을 수행했습니다.
내가 할 알고있는 것은 내가 전에 그것들을 사용했기 때문에 (적어도 C # 및 Java로) 문자 정의 구문이 정적으로 입력 된 언어로 완전하게 가능하다 개체입니다. 정적으로 입력 된 언어에서는 '개체 이니셜 라이저'라고합니다. 다음은 Java 및 C # 에서의 사용법을 보여주는 링크 입니다.
Evan Plaice가 말한 것과 마찬가지로, 가능한 경우 연관 배열 (또는 언어의 비교 가능한 데이터 구조)을 함수에 전달하는 것을 좋아합니다.
따라서 (예를 들어) 대신 :
<?php
createBlogPost('the title', 'the summary', 'the author', 'the date of publication, 'the keywords', 'the category', 'etc');
?>
간다 :
<?php
// create a hash of post data
$post_data = array(
'title' => 'the title',
'summary' => 'the summary',
'author' => 'the author',
'pubdate' => 'the publication date',
'keywords' => 'the keywords',
'category' => 'the category',
'etc' => 'etc',
);
// and pass it to the appropriate function
createBlogPost($post_data);
?>
워드 프레스는 이런 식으로 많은 일을하는데 잘 작동한다고 생각합니다. (위의 예제 코드는 상상력이 뛰어나며 Wordpress의 예제는 아닙니다.)
이 기술을 사용하면 많은 데이터를 함수에 쉽게 전달할 수 있지만 각 데이터를 전달해야하는 순서를 기억하지 않아도됩니다.
리팩토링 할 때가되면이 기술에 감사 할 것입니다. 함수 인수의 순서를 변경하지 않고 (예 : 다른 인수를 전달해야한다는 것을 알고있을 때) 함수의 매개 변수를 변경할 필요가 없습니다. 전혀 나열하지 마십시오.
이렇게하면 함수 정의를 다시 작성하지 않아도되며 함수가 호출 될 때마다 인수 순서를 변경하지 않아도됩니다. 대단한 승리입니다.
이전 답변 에서는 함수의 매개 변수가 적을수록 더 잘 수행한다고 말한 신뢰할 수있는 작성자를 언급했습니다. 대답은 왜 그 이유를 설명하지 않았지만 책은 그것을 설명합니다. 그리고 여기 당신이이 철학을 채택해야하고 내가 개인적으로 동의하는 가장 설득력있는 두 가지 이유가 있습니다.
매개 변수는 함수와 다른 추상화 레벨에 속합니다. 이것은 코드의 독자가 함수의 매개 변수의 본질과 목적에 대해 생각해야한다는 것을 의미합니다.이 생각은 이름의 의미와 해당 함수의 목적보다 "낮은 수준"입니다.
함수에 대해 가능한 한 적은 매개 변수를 갖는 두 번째 이유는 테스트입니다. 예를 들어, 10 개의 매개 변수가있는 함수가있는 경우 단위와 같이 모든 테스트 케이스를 포함해야하는 매개 변수 조합 수에 대해 생각하십시오. 테스트. 적은 매개 변수 = 적은 테스트.
Robert Martin의 "Clean Code : Handbook of Agile Software Craftsmanship"에서 함수 인수의 이상적인 수에 대한 조언에 대한 더 많은 컨텍스트를 제공하기 위해 저자는 다음과 같이 말합니다.
인수는 어렵다. 그들은 많은 개념적 힘을 취합니다. 그렇기 때문에 예제에서 거의 모든 것을 제거했습니다. 예를 들어, 예를 고려하십시오
StringBuffer
. 인스턴스 변수로 만드는 대신 인수로 전달할 수 있었지만 독자는 그것을 볼 때마다 해석해야했습니다. 모듈이 말한 이야기를 읽을includeSetupPage()
때보다 이해하기 쉽습니다includeSetupPageInto(newPageContent)
. 인수는 함수 이름과는 다른 추상화 수준에 있으며,StringBuffer
그 시점에서 특별히 중요하지 않은 세부 사항 (즉, )을 강제로 알려 줍니다.
includeSetupPage()
위의 예를 들어, 다음 장의 마지막 부분에서 리팩토링 된 "깨끗한 코드"에 대한 작은 스 니펫이 있습니다.
// *** NOTE: Commments are mine, not the author's ***
//
// Java example
public class SetupTeardownIncluder {
private StringBuffer newPageContent;
// [...] (skipped over 4 other instance variables and many very small functions)
// this is the zero-argument function in the example,
// which calls a method that eventually uses the StringBuffer instance variable
private void includeSetupPage() throws Exception {
include("SetUp", "-setup");
}
private void include(String pageName, String arg) throws Exception {
WikiPage inheritedPage = findInheritedPage(pageName);
if (inheritedPage != null) {
String pagePathName = getPathNameForPage(inheritedPage);
buildIncludeDirective(pagePathName, arg);
}
}
private void buildIncludeDirective(String pagePathName, String arg) {
newPageContent
.append("\n!include ")
.append(arg)
.append(" .")
.append(pagePathName)
.append("\n");
}
}
저자의 "스쿨 오브 스쿨"은 작은 클래스, (이상적으로는 0) 개의 함수 인수 및 매우 작은 함수를 주장합니다. 나는 또한 그에게 완전히 동의하지는 않지만 생각을 자극하는 것을 발견했으며 함수로서 제로 함수 인수에 대한 아이디어를 고려해 볼 가치가 있다고 생각합니다. 또한 위의 작은 코드 스 니펫조차도 0이 아닌 인수 함수도 있으므로 컨텍스트에 따라 다릅니다.
(그리고 다른 사람들이 지적했듯이, 그는 또한 더 많은 논증이 테스트 관점에서 어려워 진다고 주장하지만 여기서는 주로 위의 예와 제로 함수 논증에 대한 그의 이론적 근거를 강조하고 싶었습니다.)
이상적으로는 0입니다. 어떤 경우에는 한두 개는 괜찮습니다.
일반적으로 4 개 이상이 나쁜 습관입니다.
다른 사람들이 지적한 단일 책임 원칙뿐만 아니라 관점을 테스트하고 디버깅하여 생각할 수도 있습니다.
하나의 매개 변수가 있으면 값을 알고 테스트하고 오류를 찾는 것이 한 가지 요소 만 있기 때문에 상대적으로 쉽습니다. 요인을 늘리면 총 복잡성이 빠르게 증가합니다. 추상적 인 예를 들면 :
'이 날씨에 무엇을 입을 까'프로그램을 고려하십시오. 하나의 입력 온도로 무엇을 할 수 있는지 고려하십시오. 당신이 상상할 수 있듯이, 무엇을 입을 것인가의 결과는 그 한 가지 요소에 근거하여 매우 간단합니다. 이제 프로그램이 실제로 온도, 습도, 이슬점, 강수량 등을 통과 한 경우 수행 할 수있는 / 할 수있는 / 할 수있는 일을 고려하십시오.