programing

많은 인수를 메서드에 전달하기 위한 베스트 프랙티스?

goodjava 2022. 10. 22. 21:17

많은 인수를 메서드에 전달하기 위한 베스트 프랙티스?

많은 인수를 수신하는 메서드를 작성해야 하는 경우가 있습니다.예를 들어 다음과 같습니다.

public void doSomething(Object objA , Object objectB ,Date date1 ,Date date2 ,String str1 ,String str2 )
{
}

이런 문제가 발생했을 때 저는 종종 인수를 맵에 캡슐화합니다.

Map<Object,Object> params = new HashMap<Object,Object>();
params.put("objA",ObjA) ;

......

public void doSomething(Map<Object,Object> params)
{
 // extracting params 
 Object objA = (Object)params.get("objA");
 ......
 }

이것은 좋은 방법이 아닙니다.패럴람을 맵에 캡슐화하는 것은 효율의 완전한 낭비입니다.좋은 점은, 깔끔한 서명, 가장 적은 수정으로 다른 패러머를 쉽게 추가할 수 있다는 것입니다.이런 종류의 문제에 대한 베스트 프랙티스는 무엇일까요?

Bloch는 효과적인 Java, 제7장(방법), 항목 40(설계 방법 서명을 주의 깊게 함)에서 다음과 같이 기술합니다.

지나치게 긴 파라미터 목록을 단축하는 방법에는 다음 3가지가 있습니다.

  • 각각 파라미터의 서브셋만을 필요로 하는 복수의 방법으로 메서드를 분할한다.
  • 파라미터 그룹을 유지하는 도우미 클래스 생성(일반적으로 스태틱멤버 클래스)
  • 객체 구성부터 메서드 호출까지 Builder 패턴을 조정합니다.

좀 더 자세한 내용을 말씀드리면, 이 책을 사시길 권합니다. 정말 가치 있는 책입니다.

마법의 문자열 키로 지도를 사용하는 것은 좋지 않은 생각이다.컴파일 시간을 체크할 수 없게 되어 필요한 파라미터가 불분명해집니다.이를 보완하려면 매우 완벽한 문서를 작성해야 합니다.몇 주 안에 코드를 보지 않고 스트링스가 뭔지 기억할 수 있겠니?오타가 났다면?잘못된 유형을 사용하시겠습니까?코드를 실행해봐야 알 수 있어요.

대신 모델을 사용합니다.이러한 모든 파라미터의 컨테이너가 되는 클래스를 만듭니다.그러면 자바 타입의 안전성을 유지할 수 있습니다.이 오브젝트를 다른 메서드에 전달하거나 컬렉션에 넣을 수도 있습니다.

물론 매개 변수 세트가 다른 곳에서 사용되지 않거나 배포되지 않으면 전용 모델이 오버킬될 수 있습니다.균형이 잡혀있으니 상식을 이용하세요.

옵션 파라미터가 많으면 fluent API를 만들 수 있습니다.단일 메서드를 일련의 메서드로 바꿉니다.

exportWithParams().datesBetween(date1,date2)
                  .format("xml")
                  .columns("id","name","phone")
                  .table("angry_robots")
                  .invoke();

스태틱 Import를 사용하면 내부 fluent API를 만들 수 있습니다.

... .datesBetween(from(date1).to(date2)) ...

'파라미터 객체 소개'라고 합니다.여러 위치에서 동일한 매개 변수 목록을 전달한 경우 모든 매개 변수 목록을 포함하는 클래스를 만드십시오.

XXXParameter param = new XXXParameter(objA, objB, date1, date2, str1, str2);
// ...
doSomething(param);

같은 파라미터 목록을 자주 전달하지 않더라도 리팩터링을 쉽게 하면 코드 가독성이 향상되므로 항상 좋습니다.3개월 후에 코드를 보면 버그를 수정하거나 기능을 추가해야 할 때 알기 쉬워집니다.

물론 일반적인 사고방식이고, 자세한 내용을 알려주지 않았기 때문에 더 자세한 조언은 드릴 수 없습니다. :-)

우선, 나는 그 방법을 다시 적용하려고 노력할 것이다.그렇게 많은 매개변수를 사용한다면 어떤 식으로든 너무 길어질 수 있습니다.분해하면 코드가 개선되고 각 메서드에 대한 매개 변수 수가 줄어들 수 있습니다.전체 작업을 자체 클래스로 리팩터링할 수도 있습니다.둘째, 동일한 파라미터 목록의 동일한(또는 슈퍼셋)을 사용하는 다른 인스턴스를 찾습니다.인스턴스가 여러 개 있는 경우 이러한 속성이 함께 속함을 나타낼 수 있습니다.이 경우 파라미터를 유지하는 클래스를 만들고 사용합니다.마지막으로 파라미터의 개수로 인해 코드 가독성을 높이기 위해 맵 오브젝트를 작성할 가치가 있는지 여부를 평가합니다.이것은 개인적인 전화라고 생각합니다.이 솔루션에는 양쪽에서 문제가 있으며, 트레이드오프 포인트는 다를 수 있습니다.6개의 매개변수를 가지고는 하지 않을 것이다.아마 10개 정도일 겁니다(다른 방법이 먼저 효과가 없다면).

이것은 오브젝트를 작성할 때 종종 문제가 됩니다.

이 경우 Use Builder 개체 패턴은 매개 변수 목록이 크고 항상 모든 매개 변수가 필요하지 않은 경우에 잘 작동합니다.

메서드 호출에 맞게 조정할 수도 있습니다.

가독성도 크게 향상됩니다.

public class BigObject
{
  // public getters
  // private setters

  public static class Buider
  {
     private A f1;
     private B f2;
     private C f3;
     private D f4;
     private E f5;

     public Buider setField1(A f1) { this.f1 = f1; return this; }
     public Buider setField2(B f2) { this.f2 = f2; return this; }
     public Buider setField3(C f3) { this.f3 = f3; return this; }
     public Buider setField4(D f4) { this.f4 = f4; return this; }
     public Buider setField5(E f5) { this.f5 = f5; return this; }

    public BigObject build()
    {
      BigObject result = new BigObject();
      result.setField1(f1);
      result.setField2(f2);
      result.setField3(f3);
      result.setField4(f4);
      result.setField5(f5);
      return result;
    }
  }
}

// Usage:
BigObject boo = new BigObject.Builder()
  .setField1(/* whatever */)
  .setField2(/* whatever */)
  .setField3(/* whatever */)
  .setField4(/* whatever */)
  .setField5(/* whatever */)
  .build();

검증 로직을 Builder 세트에 넣을 수도 있습니다.() 및 build() 메서드입니다.

파라미터 오브젝트라고 불리는 패턴이 있습니다.

아이디어는 모든 파라미터 대신 하나의 객체를 사용하는 것입니다.나중에 파라미터를 추가해야 하는 경우에도 오브젝트에 추가하기만 하면 됩니다.이치노

해당 데이터를 저장할 클래스를 만들 수 있습니다.충분히 의미가 있어야 하지만 지도(OMG)를 사용하는 것보다 훨씬 낫습니다.

Code Complete*는 다음 사항을 제안합니다.

  • 루틴의 매개 변수 수를 약 7개로 제한합니다.7은 사람들의 이해를 위한 마법의 숫자이다.(p 108).
  • "인풋-수정-출력 순서로 파라미터를 설정합니다.여러 루틴이 유사한 파라미터를 사용하는 경우 유사한 파라미터를 일관된 순서로 배치한다.(p105)
  • 상태 또는 오류 변수를 맨 마지막에 배치합니다.
  • tvanfosson이 언급한 것처럼 루틴에 필요한 구조화된 변수(객체)의 일부만 전달합니다.즉, 함수에 구조화된 변수의 대부분을 사용하는 경우에는 구조 전체를 통과하기만 하면 되지만, 이 경우 결합이 어느 정도 촉진된다는 점에 유의하십시오.

* 초판, 업데이트해야 한다는 것을 알고 있습니다.또한, 이 조언 중 일부는 OOP가 더 인기를 끌기 시작했을 때 제2판이 작성된 이후로 바뀌었을 수 있습니다.

맵을 사용하는 것은 콜시그니처를 청소하는 간단한 방법입니다만, 다른 문제가 발생합니다.메서드의 본문을 살펴보고 메서드가 해당 맵에서 무엇을 예상하는지, 키 이름 또는 값의 유형을 확인해야 합니다.

보다 깔끔한 방법은 오브젝트빈 내의 모든 파라미터를 그룹화하는 것입니다만, 그래도 문제가 완전히 해결되는 것은 아닙니다.

여기에 있는 것은 디자인상의 문제입니다.메서드에 파라미터가 7개를 넘으면 파라미터가 나타내는 것과 순서를 기억하는 데 문제가 생기기 시작합니다.여기서부터 메서드를 잘못된 파라미터 순서로 호출하는 것만으로 많은 버그가 발생합니다.

많은 파라미터를 송신하는 베스트 프랙티스가 아닌, 보다 나은 앱 설계가 필요합니다.

좋은 관행은 리팩터링일 것이다.이 오브젝트들은 이 메서드에 전달되어야 한다는 것은 무엇을 의미합니까?단일 객체로 캡슐화해야 합니까?

bean 클래스를 만들고 모든 파라미터(세터 메서드)를 설정하고 이 bean 객체를 메서드에 전달합니다.

  • 코드를 보고 이 모든 매개 변수가 전달되는 이유를 확인합니다.때로는 메서드 자체를 리팩터링할 수 있습니다.

  • 지도를 사용하면 방법이 취약해집니다.이 메서드를 사용하는 누군가가 파라미터 이름을 잘못 입력하거나 메서드가 UDT를 예상하는 문자열을 게시하면 어떻게 됩니까?

  • Transfer Object를 정의합니다.최소한의 타입 체크를 실시합니다.사용하는 방법 내에서가 아니라 사용 시점에서 검증을 실시할 수도 있습니다.

난 네가 전에 했던 방식을 고수하고 싶어.예제에서 모수의 수는 많지 않지만 다른 모수가 훨씬 더 끔찍합니다.

  1. 지도 - 말씀하신 효율성 문제가 있습니다. 하지만 더 큰 문제는 다음과 같습니다.

    • 해야 할지 알 수
      ★★★★★★★★★★★★...와 javadocs를 ?
      이사 ??이 경우(매우 좋음) 파라미터가 많아도 문제가 되지 않습니다.
    • 다른 인수 유형을 받아들이기가 매우 어려워집니다.입력 파라미터를 1가지 타입으로 제한하거나 Map <String, Object>를 사용하여 모든 값을 캐스트할 수 있습니다.두 옵션 모두 대부분의 경우 끔찍합니다.
  2. Wrapper 객체는 처음에 Wrapper 객체를 채워야 하므로 매개 변수 객체의 생성자에게 직접 전달되지 않습니다.문제의 이동이 적절한지 여부는 해당 개체의 재사용 여부에 따라 결정됩니다.예:

사용하지 않음:첫 번째 통화에서 한 번만 사용되기 때문에 한 회선을 처리하기 위해 많은 추가 코드가...?

{
    AnObject h = obj.callMyMethod(a, b, c, d, e, f, g);
    SomeObject i = obj2.callAnotherMethod(a, b, c, h);
    FinalResult j = obj3.callAFinalMethod(c, e, f, h, i);
}

사용 가능: 여기, 조금 더 사용할 수 있습니다.우선, 3개의 메서드콜의 파라미터를 인수화할 수 있습니다.다른 두 줄도 연주할 수 있어요그래서 어떤 의미에서는 상태 변수가 되고...

{
    AnObject h = obj.callMyMethod(a, b, c, d, e, f, g);
    e = h.resultOfSomeTransformation();
    SomeObject i = obj2.callAnotherMethod(a, b, c, d, e, f, g);
    f = i.somethingElse();
    FinalResult j = obj3.callAFinalMethod(a, b, c, d, e, f, g, h, i);
}
  1. 빌더 패턴 - 제가 보기에 이것은 안티 패턴입니다.가장 바람직한 에러 처리 메커니즘은 나중에 검출하지 않고 조기에 검출하는 것입니다.그러나 빌더 패턴에서는 필수 파라미터가 누락되어 있는 콜(프로그래머가 필수 파라미터를 포함한다고 생각하지 않음)이 있는 콜이 컴파일 시간에서 실행 시간으로 이동합니다.물론 프로그래머가 의도적으로 슬롯에 null 같은 것을 삽입했다면, 그것은 런타임이지만, 여전히 더 일찍 오류를 발견하는 것은 호출하는 메서드의 파라미터 이름을 보는 것을 거부하는 프로그래머에게 훨씬 더 큰 이점입니다.많은 의 선택적 매개 변수를 다룰 때만 적절하다는 것을 알 수 있으며, 그럼에도 불구하고 이익은 기껏해야 미미합니다.나는 "패턴"이라는 건축가에 매우 반대한다.

사람들이 잊고 있는 또 다른 것은 이 모든 것에서 IDE의 역할이다.메서드에 파라미터가 있는 경우 IDE에 의해 대부분의 코드가 생성되고 빨간색 선이 표시되어 사용자가 지정/설정해야 하는 것을 알 수 있습니다.옵션 3을 사용할 때...넌 이걸 완전히 잃었어이제는 프로그래머에게 달려있고 코딩과 컴파일 시간에는 아무런 단서도 없다...프로그래머는 그것을 알아내기 위해 테스트해야 한다.

또한 옵션 2와 3은 불필요하게 광범위하게 채택될 경우 대량의 중복 코드가 생성되기 때문에 유지관리 측면에서 장기적으로 부정적인 영향을 미칩니다.코드가 많을수록 유지보수가 더 많아지고 유지보수에 더 많은 시간과 비용이 소요됩니다.

이것은 종종 여러분의 학급이 두 개 이상의 책임을 지고 있다는 것을 나타냅니다(즉, 여러분의 학급이 너무 많은 것을 하고 있습니다).

'단일 책임 원칙

자세한 것은, 을 참조해 주세요.

매개 변수를 너무 많이 전달하는 경우 메서드를 재팩터링해 보십시오.아마도 그것은 하지 말아야 할 많은 일들을 하고 있을 것이다.그렇지 않은 경우 파라미터를 단일 클래스로 대체해 보십시오.이렇게 하면 모든 것을 단일 클래스 인스턴스로 캡슐화하고 매개 변수가 아닌 인스턴스를 전달할 수 있습니다.

...그리고 밥은 너의 삼촌이다.오브젝트 작성을 위한 번거로움 없는 팬시 팬츠 API!

https://projectlombok.org/features/Builder

언급URL : https://stackoverflow.com/questions/2432443/best-practice-for-passing-many-arguments-to-method