과도한 메소드 과부하를 피하는 방법은 무엇입니까?


16

우리는 응용 프로그램의 소스 코드에 꽤 많은 장소가 있습니다. 한 클래스에는 동일한 이름과 다른 매개 변수를 가진 많은 메소드가 있습니다. 이러한 메소드에는 항상 '이전'메소드의 모든 매개 변수와 하나 이상의 매개 변수가 있습니다.

그것은 긴 진화 (레거시 코드)와 이러한 생각 (믿습니다)의 결과입니다.

" A를 수행하는 메소드 M이 있습니다. A + B를 수행해야합니다. 알겠습니다. 저는 M에 새 매개 변수를 추가하고,이를 위해 새 메소드를 작성하고, 코드를 M에서 새 메소드로 이동합니다. 하나 이상의 매개 변수로 A + B를 수행하고 새 매개 변수의 기본값으로 M에서 새 메소드를 호출하십시오. "

다음은 Java와 같은 언어의 예입니다.

class DocumentHome {

  (...)

  public Document createDocument(String name) {
    // just calls another method with default value of its parameter
    return createDocument(name, -1);
  }

  public Document createDocument(String name, int minPagesCount) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false);
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false, "");
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
    // here the real work gets done
    (...)
  }

  (...)
}

나는 이것이 잘못되었다고 생각한다. 우리는 이와 같은 새로운 매개 변수를 계속 추가 할 수있을뿐만 아니라 메소드 간의 모든 종속성으로 인해 코드를 확장 / 변경하기가 어렵습니다.

이 작업을 더 잘 수행하는 방법은 다음과 같습니다.

  1. 파라미터 객체를 소개합니다 :

    class DocumentCreationParams {
    
      String name;
      int minPagesCount;
      boolean firstPageBlank;
      String title;
    
      (...)
    }
    
    class DokumentHome {
    
      public Document createDocument(DocumentCreationParams p) {
        // here the real work gets done
        (...)
      }
    }
    
  2. DocumentHome호출하기 전에 매개 변수를 객체로 설정하십시오.createDocument()

      @In
      DocumentHome dh = null;
    
      (...)
    
      dh.setName(...);
      dh.setMinPagesCount(...);
      dh.setFirstPageBlank(...);
    
      Document newDocument = dh.createDocument();
    
  3. 작업을 다른 방법으로 분리하고 필요에 따라 호출하십시오.

      @In
      DocumentHome dh = null;
    
      Document newDocument = dh.createDocument();
      dh.changeName(newDocument, "name");
      dh.addFirstBlankPage(newDocument);
      dh.changeMinPagesCount(new Document, 10);
    

내 질문 :

  1. 설명 된 문제가 실제로 문제입니까?
  2. 제안 된 솔루션에 대해 어떻게 생각하십니까? (경험에 따라) 어느 것을 선호하십니까?
  3. 다른 해결책을 생각할 수 있습니까?

1
어떤 언어를 타겟팅하고 있습니까? 아니면 sth generell입니까?
Knerd

특별한 언어는 없습니다. 다른 언어의 기능이이를 어떻게 도울 수 있는지 자유롭게 보여주십시오.
Ytus

내가 여기에 말했듯이 programmers.stackexchange.com/questions/235096/… C # 및 C ++에는 몇 가지 기능이 있습니다.
Knerd

이 질문은 그러한 종류의 메소드 오버로드를 지원하는 모든 언어에 적용된다는 것이 분명합니다.
Doc Brown

1
@DocBrown은 괜찮지 만 모든 언어가 동일한 대안을 지원하는 것은 아닙니다.)
Knerd

답변:


20

아마도 빌더 패턴을 사용해보십시오 . (참고 : 상당히 임의의 Google 결과 :)

var document = new DocumentBuilder()
                   .FirstPageBlank()
                   .Name("doc1final(2).doc")
                   .MinimumNumberOfPages(4)
                   .Build();

귀하가 제공하는 옵션보다 빌더를 선호하는 이유에 대해 자세히 설명 할 수는 없지만 많은 코드에서 큰 문제를 식별했습니다. 메소드에 두 개 이상의 매개 변수가 필요하다고 생각되면 코드가 잘못 구성되었을 수 있습니다 (일부는 논쟁 중입니다!).

params 객체의 문제는 (만약 당신이 만든 객체가 실제와 다른 경우가 아니라면) 문제를 한 수준 위로 올려 놓고 '객체'를 형성하는 관련없는 매개 변수 클러스터로 끝납니다.

다른 시도는 누군가가 빌더 패턴에 도달하지만 제대로 도착하지 않는 것처럼 보입니다. :)


감사합니다. 답은 솔루션 수일 수 있습니다. 네. 나는 이런 식으로 답변을 더 찾고 있습니다 : '제 경험상 이것은 ...으로 이어질 수 있으며 ... 또는 '동료의 코드에서이 내용을 볼 때 대신 그에게 제안하십시오.'
Ytus

모르겠어요 ... 당신이 문제를 옮기고있는 것 같습니다. 예를 들어, 응용 프로그램의 다른 부분에서 문서를 작성할 수 있다면이 문서 구성을 분리 된 클래스 (예 : a와 같은 DocumentoFactory) 로 구성하여 테스트하는 것이 좋습니다 . 갖는 builder다른 장소에 그것을 (예 : 당, 문서에 새로운 필수 필드를 추가 같은) 문서의 건설 미래의 변화를 제어하고 단지 빌더를 사용하는 클래스에 대한 필요성 빌더 문서를 충족하기 위해 테스트에 추가 상용구 코드를 추가 어렵다.
Dherik

1

매개 변수 객체를 사용하는 것은 메소드의 (과도한) 과부하를 피하는 좋은 방법입니다.

  • 코드를 정리합니다
  • 기능과 데이터를 분리
  • 코드를 유지 관리하기 쉽게 만듭니다.

그러나 나는 너무 멀리 가지 않을 것입니다.

여기에 과부하가 있고 나쁜 것은 없습니다. 프로그래밍 언어에서 지원되므로 사용하십시오.

나는 빌더 패턴을 알지 못했지만, 우연히 "우연히"사용했다. 여기에도 동일하게 적용됩니다 : 과용하지 마십시오. 예제의 코드는 그 이점을 얻을 수 있지만 단일 오버로드 방법이있는 모든 메서드에 대해 많은 시간을 소비하는 것은 그리 효율적이지 않습니다.

그냥 내 2 센트.


0

나는 솔직히 코드에 큰 문제가 보이지 않습니다. C # 및 C ++에서는 선택적 매개 변수를 사용할 수 있습니다.하지만 대안은 아니지만 Java가 그러한 종류의 매개 변수를 지원하지 않는다는 것을 알고 있습니다.

C #에서는 모든 과부하를 비공개로 만들 수 있으며 선택적 매개 변수를 사용하는 한 가지 방법은 공개적으로 물건을 호출합니다.

귀하의 질문 2 부에 답하기 위해 매개 변수 객체 또는 사전 / 해시 맵을 사용합니다.

이렇게 :

class DokumentHome {

  public Document createDocument(Map<string, object> params) {
    if (params.containsKey("yourkey")) {
       // do that
    }
    // here the real work gets done
    (...)
  }
}

면책 조항으로, 나는 먼저 C # 및 JavaScript 프로그래머이고 Java 프로그래머입니다.


4
그 해결책이지만 좋은 해결책이라고 생각하지 않습니다 (적어도 typesafety가 예상되는 상황에서는 아닙니다).
Doc Brown

맞습니다. 그런 경우 때문에 오버로드 된 메소드 또는 선택적 매개 변수를 좋아합니다.
Knerd

2
사전을 매개 변수로 사용하는 것은 명백한 매개 변수의 수를 메소드로 줄이는 쉬운 방법이지만 메소드의 실제 종속성을 모호하게합니다. 이것은 호출자가 주석에 있거나 주석에 대한 다른 호출 또는 메소드 구현 자체에 상관없이 사전에 정확히 있어야하는 것을 다른 곳에서 찾아야합니다.
Mike Partridge

진정한 선택적 매개 변수에 대해서만 사전을 사용 하므로 기본 사용 사례는 너무 많은 문서를 읽을 필요가 없습니다.
user949300

0

이것이 빌더 패턴의 좋은 후보라고 생각합니다. 빌더 패턴은 동일한 유형이지만 다른 표현으로 오브젝트를 작성하려는 경우에 유용합니다.

귀하의 경우 다음 방법을 사용하는 빌더가 있습니다.

SetTitle (Document document) { ... }
SetFirstPageBlank (Document document) { ... }
SetMinimumPageCount (Document document) { ... }

그런 다음 빌더를 다음 방식으로 사용할 수 있습니다.

_builder.SetTitle(document);
_builder.SetFirstPageBlank(document);

참고로, 간단한 오버로드는 신경 쓰지 않습니다. 지옥, .NET 프레임 워크는 HTML 도우미로 모든 곳에서 사용합니다. 그러나 모든 메소드에 두 개 이상의 매개 변수를 전달 해야하는 경우 수행중인 작업을 다시 평가합니다.


0

builder솔루션이 대부분의 시나리오에서 작동 할 수 있다고 생각 하지만 더 복잡한 경우에는 빌더가 설정하기복잡 합니다. 메서드 순서, 실수로 노출 해야하는 메소드의 순서에 약간의 실수를 저지르기 쉽기 때문입니다. 따라서 많은 사람들이 가장 간단한 솔루션을 선호합니다.

문서를 작성하기 위해 간단한 빌더를 작성하고이 코드를 애플리케이션의 다른 부분 (클래스)에 분산 시키면 다음과 같은 작업이 어려워집니다.

  • 구성 : 다양한 방법으로 문서를 작성하는 많은 수업이 있습니다.
  • 유지 : 문서 인스턴스화에 대한 변경 (예 : 새로운 필수 제출 추가)은 Shotgun Surgery로 이어집니다.
  • 테스트 : 문서를 작성하는 클래스를 테스트하는 경우 문서 인스턴스화를 만족시키기 위해 상용구 코드를 추가해야합니다.

그러나 이것은 OP 질문에 대답하지 않습니다.

과부하의 대안

일부 대안 :

  • 메소드 이름 변경 : 동일한 메소드 이름이 혼란을 야기하는 경우 메소드 이름을 바꾸어 다음 createDocument과 같은 것보다 의미있는 이름을 더 잘 작성하십시오 . createLicenseDriveDocument,, createDocumentWithOptionalFields물론 이것은 거대한 메소드 이름으로 이어질 수 있습니다. 모든 경우에 대한 해결책.
  • 정적 메소드를 사용하십시오 . 이 접근법은 위의 첫 번째 대안과 비교하면 비슷합니다. 각 사례에 의미있는 이름을 사용하고 Document다음과 같은 정적 메소드에서 문서를 인스턴스화 할 수 있습니다 Document.createLicenseDriveDocument().
  • 공통 인터페이스 만들기 :라는 단일 메서드 createDocument(InterfaceDocument interfaceDocument)를 만들고에 대해 다른 구현 을 만들 수 있습니다 InterfaceDocument. 예를 들면 다음과 같습니다 createDocument(new DocumentMinPagesCount("name")).. 물론 각 구현에 대해 하나 이상의 구현이 필요하지 않습니다. 각 구현에 대해 둘 이상의 생성자를 생성하여 해당 구현에 적합한 일부 필드를 그룹화 할 수 있기 때문입니다. 이 패턴을 텔레 스코핑 생성자 라고 합니다.

또는 과부하 솔루션을 유지하십시오. 때로는 추악한 솔루션 임에도 불구하고 사용에 대한 결점은 많지 않습니다. 이 경우 DocumentoFactory문서를 작성 해야하는 클래스에 대한 종속성으로 삽입 할 수있는 것과 같이 분리 된 클래스에서 오버로드 메소드를 사용하는 것을 선호합니다 . 훌륭한 빌더를 작성하고 한곳에서 코드를 유지 관리하는 복잡성없이 필드 구성하고 유효성을 검사 할 수 있습니다.

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