너무 많은 (6+) 매개 변수가있는 메서드를 리팩터링하는 가장 좋은 방법은 무엇입니까?


102

때때로 불편한 매개 변수 수가있는 메소드를 발견합니다. 종종 그들은 생성자 인 것 같습니다. 더 나은 방법이 있어야 할 것 같지만 그것이 무엇인지 모르겠습니다.

return new Shniz(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

매개 변수 목록을 표현하기 위해 구조체를 사용하려고 생각했지만, 문제를 한 곳에서 다른 곳으로 옮기고 그 과정에서 다른 유형을 만드는 것 같습니다.

ShnizArgs args = new ShnizArgs(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)
return new Shniz(args);

그래서 그것은 개선 된 것 같지 않습니다. 그렇다면 최선의 접근 방식은 무엇입니까?


당신은 "구조체"라고 말했다. 이 용어는 다른 프로그래밍 언어에서 다른 의미를 가지고 있습니다. 무슨 뜻입니까?
Jay Bazuzi

1
명확하게 할 특정 언어를 찾고 있다면 C #을 사용하세요. 하지만 기본적으로 단순한 재산 가방입니다. 유형이 다른 다른 명명 된 속성이 있습니다. 클래스, 해시 테이블, 구조체 등으로 정의 할 수 있습니다.
재귀

이 기사 는 주제에 대한 좋은 통찰력을 가지고 있습니다. 자바 스크립트에 따라 다르지만 원칙은 다른 언어에 다시 적용될 수 있습니다.
lala

답변:


94

가장 좋은 방법은 인수를 함께 그룹화하는 방법을 찾는 것입니다. 이것은 여러 인수 "그룹화"로 끝날 경우에만 실제로 작동합니다.

예를 들어 직사각형에 대한 사양을 전달하는 경우 x, y, 너비 및 높이를 전달하거나 x, y, 너비 및 높이를 포함하는 직사각형 객체를 전달할 수 있습니다.

리팩토링 할 때 이와 같은 것을 찾아서 정리하십시오. 논쟁이 정말로 결합 될 수 없다면, 단일 책임 원칙을 위반했는지 살펴보기 시작하십시오.


4
좋은 생각이지만 나쁜 예입니다. Rectangle의 생성자는 4 개의 인수를 가져야합니다. 메서드가 2 세트의 직사각형 좌표 / 치수를 예상하는 경우 더 합리적입니다. 그런 다음 x1, x2, y1, y2 대신 직사각형 2 개를 전달할 수 있습니다 ...
Outlaw Programmer

2
그럴 수 있지. 내가 말했듯이, 여러 논리적 그룹이있는 경우에만 수행하는 것이 합리적입니다.
Matthew Brubaker 09-01-13

23
+1 : 단일 책임에 대한 진정한 문제를 실제로 해결하는 모든 답변에서 몇 안되는 댓글 중 하나입니다. 어떤 객체가 그 정체성을 형성하기 위해 7 개의 독립적 인 가치를 정말로 필요로합니다.
AnthonyWJones

6
@AnthonyWJones 동의하지 않습니다. 현재 기상 조건에 대한 데이터는 그 정체성을 형성하기 위해 더 많은 독립적 인 값을 가질 수 있습니다.
funct7

107

나는 당신이 C # 을 의미한다고 가정 할 것 입니다. 이 중 일부는 다른 언어에도 적용됩니다.

몇 가지 옵션이 있습니다.

생성자에서 속성 설정 자로 전환합니다 . 이것은 어떤 값이 어떤 매개 변수에 해당하는지 독자에게 분명하기 때문에 코드를 더 읽기 쉽게 만들 수 있습니다. 객체 이니셜 라이저 구문은이를 멋지게 만듭니다. 자동 생성 된 속성을 사용하고 생성자 작성을 건너 뛸 수 있으므로 구현도 간단합니다.

class C
{
    public string S { get; set; }
    public int I { get; set; }
}

new C { S = "hi", I = 3 };

그러나 불변성을 잃고 컴파일 타임에 개체를 사용하기 전에 필요한 값이 설정되었는지 확인하는 기능을 잃게됩니다.

빌더 패턴 .

사이의 관계에 대해 생각 string하고 StringBuilder. 자신의 수업을 위해 이것을 얻을 수 있습니다. 나는 그것을 중첩 클래스로 구현하는 것을 좋아하므로 클래스 C에는 관련 클래스가 C.Builder있습니다. 나는 또한 빌더의 유창한 인터페이스를 좋아합니다. 올바르게 완료하면 다음과 같은 구문을 얻을 수 있습니다.

C c = new C.Builder()
    .SetX(4)    // SetX is the fluent equivalent to a property setter
    .SetY("hello")
    .ToC();     // ToC is the builder pattern analog to ToString()

// Modify without breaking immutability
c = c.ToBuilder().SetX(2).ToC();

// Still useful to have a traditional ctor:
c = new C(1, "...");

// And object initializer syntax is still available:
c = new C.Builder { X = 4, Y = "boing" }.ToC();

이 모든 작업을 수행하는 빌더 코드를 생성 할 수있는 PowerShell 스크립트가 있는데, 입력은 다음과 같습니다.

class C {
    field I X
    field string Y
}

그래서 컴파일 타임에 생성 할 수 있습니다. partial클래스를 사용하면 생성 된 코드를 수정하지 않고도 기본 클래스와 빌더를 모두 확장 할 수 있습니다.

"Introduce Parameter Object"리팩토링 . 리팩토링 카탈로그를 참조하십시오 . 아이디어는 전달하려는 매개 변수 중 일부를 가져 와서 새 유형에 넣은 다음 대신 해당 유형의 인스턴스를 전달하는 것입니다. 생각하지 않고이 작업을 수행하면 시작했던 위치로 돌아갑니다.

new C(a, b, c, d);

된다

new C(new D(a, b, c, d));

그러나이 접근 방식은 코드에 긍정적 인 영향을 미칠 수있는 가장 큰 잠재력을 가지고 있습니다. 따라서 다음 단계를 따라 계속하십시오.

  1. 함께 의미가있는 매개 변수의 하위 집합 을 찾습니다 . 함수의 모든 매개 변수를 무의식적으로 그룹화하는 것만으로는별로 도움이되지 않습니다. 목표는 의미있는 그룹을 만드는 것입니다. 새로운 유형의 이름이 분명 할 때 올바른 것을 알 수 있습니다.

  2. 이러한 값이 함께 사용되는 다른 위치를 찾고 거기에서도 새 유형을 사용하십시오. 이미 모든 곳에서 사용하고있는 일련의 값에 대해 좋은 새 유형을 찾은 경우 해당 새 유형이 모든 위치에서도 의미가있을 것입니다.

  3. 기존 코드에 있지만 새 유형에 속하는 기능을 찾으십시오.

예를 들어 다음과 같은 코드가 표시 될 수 있습니다.

bool SpeedIsAcceptable(int minSpeed, int maxSpeed, int currentSpeed)
{
    return currentSpeed >= minSpeed & currentSpeed < maxSpeed;
}

minSpeedmaxSpeed매개 변수를 가져 와서 새 유형에 넣을 수 있습니다.

class SpeedRange
{
   public int Min;
   public int Max;
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return currentSpeed >= sr.Min & currentSpeed < sr.Max;
}

이 방법이 더 좋지만 새 유형을 실제로 활용하려면 비교를 새 유형으로 이동하십시오.

class SpeedRange
{
   public int Min;
   public int Max;

   bool Contains(int speed)
   {
       return speed >= min & speed < Max;
   }
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return sr.Contains(currentSpeed);
}

그리고 이제 우리는 어딘가에 있습니다. SpeedIsAcceptable()이제 구현 이 의미하는 바를 말하고 유용하고 재사용 가능한 클래스를 갖게됩니다. (다음 명백한 단계는 만드는 것입니다 SpeedRangeRange<Speed> .)

보시다시피 Introduce Parameter Object는 좋은 시작 이었지만 실제 가치는 모델에서 누락 된 유용한 유형을 발견하는 데 도움이되었다는 것입니다.


4
먼저 "Introduce Parameter Object"를 시도하고 생성 할 좋은 매개 변수 개체를 찾을 수없는 경우에만 다른 옵션으로 대체하는 것이 좋습니다.
Douglas Leeder

4
훌륭한 대답입니다. C # 구문 설탕 이전에 리팩토링 설명을 언급했다면 더 높은 IMHO로 선정되었을 것입니다.
rpattabi 2010

10
오! +1 "새로운 유형의 이름이 분명 할 때 제대로 된 것입니다."
Sean McMillan

20

생성자 인 경우, 특히 오버로드 된 변형이 여러 개있는 경우 빌더 패턴을 살펴 봐야합니다.

Foo foo = new Foo()
          .configBar(anything)
          .configBaz(something, somethingElse)
          // and so on

정상적인 방법이라면 전달되는 값 간의 관계에 대해 생각하고 전송 객체를 만들어야합니다.


훌륭한 답변입니다. 나를 포함하여 모든 사람이 제공 한 "수업에 매개 변수 입력"응답보다 더 관련성이있을 수 있습니다.
Wouter Lievens

1
생성자에 너무 많은 매개 변수를 전달하지 않기 위해 클래스를 변경 가능하게 만드는 것은 아마도 나쁜 생각 일 것입니다.
Outlaw Programmer

@outlaw-변경 가능성이 우려되는 경우 "한 번 실행"의미 체계를 쉽게 구현할 수 있습니다. 그러나 많은 수의 ctor 매개 변수는 종종 구성이 필요함을 나타냅니다 (또는 다른 사람들이 언급했듯이 너무 많은 작업을 수행하려는 클래스). (계속)
kdgregory

구성을 외부화 할 수는 있지만, 특히 프로그램 상태에 의해 구동되거나 주어진 프로그램에 대한 표준 인 경우에는 필요하지 않습니다 (네임 스페이스를 인식 할 수있는 XML 파서를 생각하고 다른 도구로 유효성 검사 및 & c).
kdgregory

나는 빌더 패턴을 좋아하지만 문자열 / StringBuilder와 같은 변경 불가능한 빌더 유형과 변경 가능한 빌더 유형을 분리하지만 중첩 클래스 인 Foo / Foo.Builder를 사용합니다. 간단한 데이터 클래스에 대해이 작업을 수행하는 코드를 생성하는 PowerShell 스크립트가 있습니다.
Jay Bazuzi

10

이에 대한 고전적인 대답은 클래스를 사용하여 매개 변수의 일부 또는 전부를 캡슐화하는 것입니다. 이론 상으로는 훌륭하게 들리지만, 나는 영역에서 의미가있는 개념에 대한 수업을 만드는 사람이기 때문에이 조언을 적용하는 것이 항상 쉬운 것은 아닙니다.

예 : 대신 :

driver.connect(host, user, pass)

당신은 사용할 수 있습니다

config = new Configuration()
config.setHost(host)
config.setUser(user)
config.setPass(pass)
driver.connect(config)

YMMV


5
나는 확실히 첫 번째 코드를 더 원한다. 나는 numbe rof 매개 변수가 추악 해지는 특정 제한이 있다는 데 동의하지만 내 취향에는 3이 허용됩니다.
blabla999

10

이것은 Fowler와 Beck의 책 "리팩토링"에서 인용되었습니다.

긴 매개 변수 목록

프로그래밍 초기에 우리는 루틴에 필요한 모든 것을 매개 변수로 전달하도록 배웠습니다. 대안은 글로벌 데이터이고 글로벌 데이터는 악하고 일반적으로 고통스럽기 때문에 이해할 수 있습니다. 필요한 것이 없으면 언제든지 다른 물건에게 가져다달라고 요청할 수 있기 때문에 물건은이 상황을 바꿉니다. 따라서 객체를 사용하면 메서드에 필요한 모든 것을 전달하지 않습니다. 대신 메서드가 필요한 모든 것을 얻을 수 있도록 충분히 전달합니다. 메서드에 필요한 많은 것은 메서드의 호스트 클래스에서 사용할 수 있습니다. 객체 지향 프로그램에서 매개 변수 목록은 기존 프로그램보다 훨씬 작은 경향이 있습니다. 긴 매개 변수 목록은 이해하기 어렵고 일관성이없고 사용하기 어렵 기 때문에 좋습니다. 더 많은 데이터가 필요할 때 영원히 변경하기 때문입니다. 새 데이터를 가져 오기 위해 몇 번만 요청하면되기 때문에 객체를 전달하면 대부분의 변경 사항이 제거됩니다. 이미 알고있는 객체를 요청하여 하나의 매개 변수에서 데이터를 가져올 수있는 경우 매개 변수를 메소드로 바꾸기를 사용하십시오. 이 개체는 필드이거나 다른 매개 변수 일 수 있습니다. 전체 개체 유지를 사용하여 개체에서 수집 한 데이터를 가져와 개체 자체로 바꿉니다. 논리 개체가없는 데이터 항목이 여러 개있는 경우 매개 변수 개체 소개를 사용합니다. 이러한 변경에는 한 가지 중요한 예외가 있습니다. 이것은 호출 된 개체에서 더 큰 개체로의 종속성을 명시 적으로 만들지 않으려는 경우입니다. 이러한 경우 데이터의 압축을 풀고 매개 변수로 보내는 것이 합리적이지만 관련된 고통에주의를 기울이십시오. 매개 변수 목록이 너무 길거나 너무 자주 변경되면 종속성 구조를 재고해야합니다.


7

나는 현명한 균열처럼 들리고 싶지 않지만 전달하는 데이터가 실제로 전달되어야 하는지 확인 해야합니다. 생성자 (또는 해당 문제에 대한 메서드)에 물건을 전달하는 것은 약간의 냄새가납니다. 행동 에 대한 약간의 강조 물체 에 .

오해하지 마세요 : 메서드와 생성자 때때로 많은 매개 변수를 가질 입니다. 그러나 발생하면 행동으로 데이터 를 캡슐화하는 것을 고려하십시오. 대신 .

이런 종류의 냄새 (우리가 리팩토링에 대해 이야기하고 있기 때문에이 끔찍한 단어가 적절 해 보입니다 ...)는 속성이나 getter / setter가 많은 객체에 대해서도 감지 될 수 있습니다.


7

긴 매개 변수 목록을 볼 때 첫 번째 질문은이 함수 또는 객체가 너무 많이 수행하고 있는지 여부입니다. 치다:

EverythingInTheWorld earth=new EverythingInTheWorld(firstCustomerId,
  lastCustomerId,
  orderNumber, productCode, lastFileUpdateDate,
  employeeOfTheMonthWinnerForLastMarch,
  yearMyHometownWasIncorporated, greatGrandmothersBloodType,
  planetName, planetSize, percentWater, ... etc ...);

물론이 예제는 의도적으로 우스꽝 스럽지만 약간 덜 어리석은 예제가있는 실제 프로그램을 많이 보았습니다. 하나의 클래스가 거의 관련이 없거나 관련이없는 많은 것을 보유하는 데 사용되는 경우, 동일한 호출 프로그램이 둘 다 필요하거나 프로그래머는 우연히 두 가지를 동시에 생각했습니다. 때로는 쉬운 해결책은 클래스를 여러 조각으로 나누는 것입니다.

클래스가 실제로 고객 주문 및 고객에 대한 일반 정보와 같은 여러 논리적 일을 처리해야하는 경우 조금 더 복잡합니다. 이 경우 고객 용 클래스와 주문 용 클래스를 상자에 넣고 필요에 따라 서로 대화하게하십시오. 그래서 대신 :

 Order order=new Order(customerName, customerAddress, customerCity,
   customerState, customerZip,
   orderNumber, orderType, orderDate, deliveryDate);

우리는 다음을 가질 수 있습니다.

Customer customer=new Customer(customerName, customerAddress,
  customerCity, customerState, customerZip);
Order order=new Order(customer, orderNumber, orderType, orderDate, deliveryDate);

물론 저는 1 개 또는 2 개 또는 3 개의 매개 변수를 취하는 함수를 선호하지만, 때때로 우리는 현실적으로이 함수가 무리를 취하고 그 자체의 숫자가 실제로 복잡성을 생성하지 않는다는 것을 받아 들여야합니다. 예를 들면 :

Employee employee=new Employee(employeeId, firstName, lastName,
  socialSecurityNumber,
  address, city, state, zip);

예, 이것은 많은 필드이지만 아마도 우리가 할 일은 데이터베이스 레코드에 저장하거나 화면에 던지는 것입니다. 여기에는 실제로 많은 처리가 없습니다.

매개 변수 목록이 길어지면 필드에 다른 데이터 유형을 제공 할 수있는 것이 훨씬 더 좋습니다. 다음과 같은 기능을 볼 때와 같습니다.

void updateCustomer(String type, String status,
  int lastOrderNumber, int pastDue, int deliveryCode, int birthYear,
  int addressCode,
  boolean newCustomer, boolean taxExempt, boolean creditWatch,
  boolean foo, boolean bar);

그리고 다음과 같이 호출됩니다.

updateCustomer("A", "M", 42, 3, 1492, 1969, -7, true, false, false, true, false);

나는 걱정된다. 전화를 보면 이러한 모든 비밀 번호, 코드 및 플래그가 의미하는 바가 전혀 명확하지 않습니다. 이것은 단지 오류를 요구하는 것입니다. 프로그래머는 매개 변수의 순서에 대해 쉽게 혼란스러워하고 실수로 두 개를 전환 할 수 있으며, 동일한 데이터 유형 인 경우 컴파일러는이를 수락합니다. 필자는이 모든 것이 열거 형인 서명을 선호하므로 호출은 "A"대신 Type.ACTIVE 및 "false"대신 CreditWatch.NO 등으로 전달됩니다.


5

생성자 매개 변수 중 일부가 선택 사항 인 경우 생성자에서 필수 매개 변수를 가져오고 빌더를 리턴하는 선택 사항에 대한 메소드가 다음과 같이 사용되는 빌더를 사용하는 것이 좋습니다.

return new Shniz.Builder(foo, bar).baz(baz).quux(quux).build();

이에 대한 자세한 내용은 Effective Java, 2nd Ed., p. 11. 메서드 인수의 경우 동일한 책 (189 페이지)에서 매개 변수 목록을 줄이는 세 가지 방법을 설명합니다.

  • 메서드를 더 적은 인수를 사용하는 여러 메서드로 나눕니다.
  • 매개 변수 그룹을 나타 내기 위해 정적 도우미 멤버 클래스를 만듭니다. 즉, and DinoDonkey대신 a 를 전달합니다.dinodonkey
  • 매개 변수가 선택 사항 인 경우 위의 빌더를 메소드에 채택하여 모든 매개 변수에 대한 객체를 정의하고 필요한 매개 변수를 설정 한 다음 실행 메소드를 호출 할 수 있습니다.

4

기본 생성자와 속성 설정자를 사용합니다. C # 3.0에는이를 자동으로 수행 할 수있는 멋진 구문이 있습니다.

return new Shniz { Foo = foo,
                   Bar = bar,
                   Baz = baz,
                   Quuz = quux,
                   Fred = fred,
                   Wilma = wilma,
                   Barney = barney,
                   Dino = dino,
                   Donkey = donkey
                 };

코드 개선은 생성자를 단순화하고 다양한 조합을 지원하기 위해 여러 메서드를 지원하지 않아도됩니다. "호출"구문은 여전히 ​​약간 "소설"이지만 속성 설정자를 수동으로 호출하는 것보다 나쁘지는 않습니다.


2
이것은 객체 t new Shniz ()가 존재하도록 허용합니다. 좋은 OO 구현은 객체가 불완전한 상태로 존재할 가능성을 최소화하려고합니다.
AnthonyWJones

일반적으로 기본 해시 / 사전 구문을 사용하는 모든 언어는 명명 된 매개 변수에 대한 적절한 대체물과 함께 제공됩니다 (위대하고 종종 이러한 상황에서 요구하는 것이지만 어떤 이유로이를 지원하는 유일한 인기 언어는 지구상에서 최악의 언어입니다) .
혼돈

4

좋은 답변을 보장하기에 충분한 정보를 제공하지 않았습니다. 긴 매개 변수 목록은 본질적으로 나쁘지 않습니다.

Shniz (foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

다음과 같이 해석 될 수 있습니다.

void Shniz(int foo, int bar, int baz, int quux, int fred, 
           int wilma, int barney, int dino, int donkey) { ...

이 경우 컴파일러가 확인하고 시각적으로 코드를 읽기 쉽게 만드는 방식으로 다른 매개 변수에 의미를 부여하기 때문에 매개 변수를 캡슐화하는 클래스를 만드는 것이 훨씬 낫습니다. 또한 나중에 더 쉽게 읽고 리팩토링 할 수 있습니다.

// old way
Shniz(1,2,3,2,3,2,1,2);
Shniz(1,2,2,3,3,2,1,2); 

//versus
ShnizParam p = new ShnizParam { Foo = 1, Bar = 2, Baz = 3 };
Shniz(p);

또는 다음과 같은 경우 :

void Shniz(Foo foo, Bar bar, Baz baz, Quux quux, Fred fred, 
           Wilma wilma, Barney barney, Dino dino, Donkey donkey) { ...

이것은 모든 객체가 다르기 때문에 (그리고 헷갈릴 가능성이 없기 때문에) 완전히 다른 경우입니다. 모든 객체가 필요하고 모두 다르면 매개 변수 클래스를 만드는 것이별로 의미가 없다는 데 동의했습니다.

또한 일부 매개 변수는 선택 사항입니까? 메서드 재정의가 있습니까 (동일한 메서드 이름이지만 다른 메서드 서명이 있습니까?) 이러한 종류의 세부 정보는 모두 가장 좋은 것이 무엇인지에 대해 중요합니다. 답변이 합니다.

* 속성 가방도 유용 할 수 있지만 배경이 없다는 점을 고려할 때 특별히 더 좋지는 않습니다.

보시다시피이 질문에 대한 정답은 1 개 이상입니다. 선택해라.



2

나는 일반적으로 구조체 접근 방식에 의지 할 것입니다. 아마도 이러한 매개 변수의 대부분은 어떤 식 으로든 관련되어 있으며 귀하의 방법과 관련된 일부 요소의 상태를 나타냅니다.

매개 변수 집합을 의미있는 개체로 만들 수없는 경우 이는 Shniz너무 많은 작업을 수행 하는 신호일 수 있으며 리팩토링에는 메서드를 별도의 문제로 세분화해야합니다.


2

소스 코드 라인의 복잡성을 교환 할 수 있습니다. 방법 자체가 너무 많은 작업을 수행하는 경우 (스위스 나이프) 다른 방법을 생성하여 작업을 절반으로 줄이십시오. 메서드가 단순하기 만하면 너무 많은 매개 변수가 필요하면 소위 매개 변수 객체라고하는 것이 좋습니다.


2

언어가 지원하는 경우 명명 된 매개 변수를 사용하고 가능한 한 많은 옵션 (적당한 기본값 사용)을 만드십시오.


1

설명하신 방법이 갈 길이라고 생각합니다. 매개 변수가 많은 메소드 및 / 또는 나중에 더 많이 필요할 가능성이있는 메소드를 찾으면 일반적으로 설명하는 것처럼 통과 할 ShnizParams 객체를 만듭니다.


1

생성자에서 한꺼번에 설정하지 않고 속성 / 세터 를 통해 수행하는 것은 어떻습니까? 클래스와 같은이 접근 방식을 활용하는 몇 가지 .NET 클래스를 보았습니다 Process.

        Process p = new Process();

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.FileName = "cmd";
        p.StartInfo.Arguments = "/c dir";
        p.Start();

3
C # 3에는 실제로이 작업을 쉽게 수행 할 수있는 구문 인 개체 이니셜 라이저가 있습니다.
Daren Thomas

1

매개 변수를 매개 변수 객체 (구조체)로 이동하는 방법에 동의합니다. 하나의 개체에 모두 고정하는 대신 다른 함수가 유사한 매개 변수 그룹을 사용하는지 검토하십시오. 매개 변수 개체는 매개 변수 집합이 해당 함수에서 일관되게 변경 될 것으로 예상되는 여러 함수와 함께 사용하는 경우 더 유용합니다. 일부 매개 변수 만 새 매개 변수 객체에 넣을 수 있습니다.


1

매개 변수가 너무 많으면 메서드가 너무 많이 수행 할 가능성이 있으므로 먼저 메서드를 여러 개의 작은 메서드로 분할하여이 문제를 해결하십시오. 이 후에도 매개 변수가 너무 많으면 인수를 그룹화하거나 일부 매개 변수를 인스턴스 멤버로 바꾸십시오.

대규모보다 소규모 수업 / 방법을 선호합니다. 단일 책임 원칙을 기억하십시오.


인스턴스 멤버 및 속성의 문제는 1) 쓰기 가능해야하고 2) 설정되지 않을 수 있다는 것입니다. 생성자의 경우 인스턴스가 존재하기 전에 채워 졌는지 확인하려는 특정 필드가 있습니다.
재귀 적

@recursive-필드 / 속성이 항상 쓰기 가능해야한다는 데 동의하지 않습니다. 소규모 클래스의 경우 읽기 전용 멤버가 의미가있는 경우가 많습니다.
Brian Rasmussen

1

명명 된 인수는 긴 (또는 심지어 짧은!) 매개 변수 목록을 명확하게하는 동시에 클래스의 속성을 (생성자의 경우) 존재하도록 허용하지 않고 변경할 수 없도록 허용하는 좋은 옵션 (지원하는 언어로 가정)입니다. 부분적으로 구성된 상태입니다.

이런 종류의 리팩터링을 수행 할 때 내가 찾는 다른 옵션은 독립적 인 개체로 더 잘 처리 될 수있는 관련 매개 변수 그룹입니다. 예를 들어 이전 답변의 Rectangle 클래스를 사용하면 x, y, 높이 및 너비에 대한 매개 변수를 사용하는 생성자는 x 및 y를 Point 객체로 인수하여 Rectangle의 생성자에 세 개의 매개 변수를 전달할 수 있습니다. 또는 조금 더 나아가 두 개의 매개 변수 (UpperLeftPoint, LowerRightPoint)로 만드십시오. 그러나 그것은보다 급진적 인 리팩토링이 될 것입니다.


0

어떤 종류의 인수가 있는지에 따라 다르지만 부울 값 / 옵션이 많은 경우 Flag Enum을 사용할 수 있습니까?


0

나는 문제가 수업에서 풀고 자하는 문제의 영역과 깊이 관련이 있다고 생각합니다.

어떤 경우에는 7 개 매개 변수 생성자가 잘못된 클래스 계층 구조를 나타낼 수 있습니다.이 경우 위에서 제안한 도우미 구조체 / 클래스는 일반적으로 좋은 접근 방식이지만 속성 백일 뿐인 구조체 부하로 끝나는 경향이 있습니다. 유용한 일을하지 마십시오. 8 인수 생성자는 클래스가 너무 일반적이거나 너무 다목적이므로 실제로 유용하려면 많은 옵션이 필요함을 나타낼 수도 있습니다. 이 경우 클래스를 리팩터링하거나 실제 복잡한 생성자를 숨기는 정적 생성자를 구현할 수 있습니다. 예 : Shniz.NewBaz (foo, bar)는 실제로 올바른 매개 변수를 전달하는 실제 생성자를 호출 할 수 있습니다.


0

한 가지 고려 사항은 개체가 생성 된 후 읽기 전용이되는 값은 무엇입니까?

공개적으로 쓰기 가능한 속성은 생성 후에 할당 될 수 있습니다.

궁극적으로 가치는 어디에서 오는가? 아마도 일부 값은 실제로는 일부 값이 실제로는 일부 구성 또는 라이브러리에서 유지 관리하는 전역 데이터에서 가져온 외부 값일 수 있습니다.

이 경우 생성자를 외부 사용으로부터 숨기고 생성 기능을 제공 할 수 있습니다. create 함수는 실제 외부 값을 가져와 객체를 생성 한 다음 라이브러리에서 사용할 수있는 접근 자만 사용하여 객체 생성을 완료합니다.

객체에 완전한 상태를 부여하고 모두 본질적으로 외부에있는 7 개 이상의 매개 변수가 필요한 객체를 갖는 것은 정말 이상 할 것입니다.


0

clas에 인수가 너무 많은 생성자가 있으면 일반적으로 책임이 너무 많다는 신호입니다. 동일한 기능을 제공하기 위해 협력하는 별도의 클래스로 나눌 수 있습니다.

생성자에 정말 많은 인수가 필요한 경우 Builder 패턴이 도움이 될 수 있습니다. 목표는 여전히 모든 인수를 생성자에 전달하는 것이므로 해당 상태는 처음부터 초기화되며 필요한 경우 클래스를 변경 불가능하게 만들 수 있습니다.

아래 참조 :

public class Toto {
    private final String state0;
    private final String state1;
    private final String state2;
    private final String state3;

    public Toto(String arg0, String arg1, String arg2, String arg3) {
        this.state0 = arg0;
        this.state1 = arg1;
        this.state2 = arg2;
        this.state3 = arg3;
    }

    public static class TotoBuilder {
        private String arg0;
        private String arg1;
        private String arg2;
        private String arg3;

        public TotoBuilder addArg0(String arg) {
            this.arg0 = arg;
            return this;
        }
        public TotoBuilder addArg1(String arg) {
            this.arg1 = arg;
            return this;
        }
        public TotoBuilder addArg2(String arg) {
            this.arg2 = arg;
            return this;
        }
        public TotoBuilder addArg3(String arg) {
            this.arg3 = arg;
            return this;
        }

        public Toto newInstance() {
            // maybe add some validation ...
            return new Toto(this.arg0, this.arg1, this.arg2, this.arg3);
        }
    }

    public static void main(String[] args) {
        Toto toto = new TotoBuilder()
            .addArg0("0")
            .addArg1("1")
            .addArg2("2")
            .addArg3("3")
            .newInstance();
    }

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