인터페이스를 구현하지만 다른 클래스는 구현하지 않는 한 클래스에서만 사용 가능한 메소드를 호출하는 더 좋은 방법은 무엇입니까?


11

기본적으로 특정 조건에 따라 다른 작업을 실행해야합니다. 기존 코드는 이런 식으로 작성됩니다

기본 인터페이스

// DoSomething.java
interface DoSomething {

   void letDoIt(String info);
}

첫 번째 노동자 계급의 이행

class DoItThisWay implements DoSomething {
  ...
}

두 번째 노동자 계급의 이행

class DoItThatWay implements DoSomething {
   ...
}

메인 클래스

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          worker = new DoItThisWay();
        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

이 코드는 정상적으로 작동하며 이해하기 쉽습니다.

이제 새로운 요구 사항으로 인해 타당한 새로운 정보를 전달해야합니다 DoItThisWay.

내 질문은 :이 요구 사항을 처리하는 데 다음 코딩 스타일이 좋습니까?

새로운 클래스 변수 및 메소드 사용

// Use new class variable and method

class DoItThisWay implements DoSomething {
  private int quality;
  DoSomething() {
    quality = 0;
  }

  public void setQuality(int quality) {
    this.quality = quality;
  };

 public void letDoIt(String info) {
   if (quality > 50) { // make use of the new information
     ...
   } else {
     ...
   }
 } ;

}

이렇게하면 발신자에게 해당하는 변경이 필요합니다.

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          int quality = obtainQualityInfo();
          DoItThisWay tmp = new DoItThisWay();
          tmp.setQuality(quality)
          worker = tmp;

        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

좋은 코딩 스타일입니까? 아니면 그냥 던져도 될까요

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          int quality = obtainQualityInfo();
          worker = new DoItThisWay();
          ((DoItThisWay) worker).setQuality(quality)
        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

19
왜 quality생성자에 전달하지 DoItThisWay않습니까?
— David Arno

아마 때문에 성능 이유로 ... 내 질문에 개정의 건설을 필요로 DoItThisWay하고 DoItThatWay의 생성자에 한 번 수행됩니다 Main. Main오래 사는 클래스이며 doingIt여러 번 반복해서 부릅니다.
— Anthony Kong

이 경우 질문을 업데이트하는 것이 좋습니다.
— David Arno

객체 setQuality의 수명 동안 메소드가 여러 번 호출 된다는 것을 의미 DoItThisWay합니까?
— jpmc26 2016 년

1
제시 한 두 버전 사이에는 관련이 없습니다. 논리적 관점에서, 그들은 같은 일을합니다.
— Sebastian Redl

답변:


6

에 대한 quality각 letDoIt()호출 과 함께 설정해야 한다고 가정합니다 DoItThisWay().

내가 여기에서 발생하는 문제는 이것입니다 : 당신은 시간적 결합을 도입하고 있습니다 (즉 , ? 를 호출 setQuality()하기 전에 전화 를 잊어 버린 경우에 발생합니다 ). 그리고 구현 이 분기 되고 있습니다 (하나는 호출 해야 하고 다른 하나는 호출하지 않아야 함).letDoIt()DoItThisWayDoItThisWayDoItThatWaysetQuality()

현재로서는 문제가 발생하지 않지만 결국에는 문제가 발생할 수 있습니다. letDoIt()품질 정보가 info당신이 그것을 통과 하는 사람의 일부가 될 필요가 있는지 다시 한 번 살펴보고 고려하는 것이 좋습니다 . 그러나 이것은 세부 사항에 달려 있습니다.


15

좋은 코딩 스타일입니까?

내 관점에서 볼 때 귀하의 버전 중 어느 것도 아닙니다. 호출 setQuality되기 전에 먼저 호출해야하는 letDoIt것은 시간적 결합 이다. DoItThisWay의 파생물로 보지 DoSomething못했지만 (적어도 기능적으로는 아닙니다) 오히려

interface DoSomethingWithQuality {
   void letDoIt(String info, int quality);
}

이것은 Main오히려 같은 것을 만들 것입니다

class Main {
    // omitting the creation
    private DoSomething doSomething;
    private DoSomethingWithQuality doSomethingWithQuality;

    public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
            int quality = obtainQualityInfo();
            doSomethingWithQuality.letDoIt(info, quality);
        } else {
            doSomething.letDoIt(info);
        }
    }
}

반면에 매개 변수를 클래스에 직접 전달하고 (가능한 경우) 팩토리에 사용할 결정을 위임 할 수 있습니다 (이 경우 인스턴스를 다시 교환 할 수 있으며 둘 다에서 파생 될 수 있음 DoSomething). 이것은 Main다음과 같이 보일 것입니다

class Main {
    private DoSomethingFactory doSomethingFactory;

     public doingIt(String info) {
         int quality = obtainQualityInfo();
         DoSomething doSomethingWorker = doSomethingFactory.Create(info, quality);
         doSomethingWorker.letDoIt();
     }
}

당신이 쓴 것을 알고 있습니다

성능상의 이유로 DoItThisWay 및 DoItThatWay의 구성은 Main의 생성자에서 한 번 수행되므로

그러나 팩토리에서 생성하는 데 비용이 많이 드는 부품을 캐시하여 생성자에게 전달할 수도 있습니다.


아아, 내가 대답을 입력하는 동안 나를 감시하고 있습니까? 우리는 이와 유사한 점을 제시합니다 (그러나 귀하의
— 주장

1
@DocBrown 예, 논쟁의 여지가 있지만, DoItThisWay전화하기 전에 품질을 설정해야하기 letDoIt때문에 다르게 동작합니다. 나는 DoSomething모든 파생 상품에 대해 동일한 방식으로 작동 할 것으로 기대합니다 (전 품질을 설정하지 않음). 이게 말이 돼? 물론 setQuality팩토리 메소드를 호출 하면 클라이언트는 품질을 설정해야하는지 여부에 대해 무의미합니다.
— Paul Kertscher 2016 년

2
@DocBrown 계약 letDoIt()은 (추가 정보가 없음) "나는 당신이 나에게 제공하면 올바르게 실행할 수 있습니다 (내가 무엇이든 할 수 있음)"라고 가정합니다 String info. 그 요구 setQuality()라는 사전 호출의 전제 조건을 강화한다 letDoIt(), 이렇게하는 것은 LSP의 위반이 될 것입니다.
— CharonX 2016 년

2
@CharonX : 예를 들어, " letDoIt"가 예외를 발생시키지 않을 것을 암시하는 계약이 있고, "setQuality"를 호출하는 것을 잊어 버리면 letDoIt예외를 던진다면, 그러한 구현이 LSP를 위반할 것이라는 데 동의합니다. 그러나 그것들은 나에게 매우 인공적인 가정처럼 보입니다.
— Doc Brown

1
이것은 좋은 대답입니다. 내가 추가 할 유일한 것은 시간 기반 커플 링이 코드베이스에 얼마나 비참한 지에 대한 논평입니다. 외관상 분리 된 구성 요소 사이의 숨겨진 계약입니다. 정상적인 커플 링을 사용하면 최소한 명시적이고 쉽게 식별 할 수 있습니다. 이렇게하면 본질적으로 구덩이를 파고 잎으로 덮습니다.
— JimmyJames 2016 년

10

quality생성자에 전달할 수 없으며 호출 setQuality이 필요 하다고 가정 해 봅시다 .

현재 코드 스 니펫은

    int quality = obtainQualityInfo();
    worker = new DoItThisWay();
    ((DoItThisWay) worker).setQuality(quality);

너무 많은 생각을 투자하기에는 너무 작습니다. IMHO 조금 추한 것처럼 보이지만 실제로 이해하기는 어렵지 않습니다.

이러한 코드 스 니펫이 커지고 리팩토링으로 인해 문제가 발생합니다.

    int quality = obtainQualityInfo();
    worker = CreateAWorkerForThisInfo();
    ((DoItThisWay) worker).setQuality(quality);

이제 해당 코드가 여전히 올바른지 즉시 확인할 수 없으며 컴파일러에서 알려주지 않습니다. 따라서 올바른 유형의 임시 변수를 도입하고 캐스트를 피하는 것은 실제적인 추가 노력없이 조금 더 안전합니다.

그러나 실제로 tmp더 나은 이름을 지정합니다.

      int quality = obtainQualityInfo();
      DoItThisWay workerThisWay = new DoItThisWay();
      workerThisWay.setQuality(quality)
      worker = workerThisWay;

이러한 이름은 잘못된 코드 를 잘못 보이게 하는 데 도움이됩니다 .


TMP는 더 나은 "workerThatUsesQuality"같은 이름이 될 수있다
— user949300

1
@ user949300 : IMHO는 좋은 생각이 아닙니다. DoItThisWay더 구체적인 매개 변수를 얻는다고 가정하십시오 -제안한대로 이름을 매번 변경해야합니다. 사용 가능한 메소드 이름이 아닌 오브젝트 유형을 명확하게하는 이름 vor 변수를 사용하는 것이 좋습니다.
— Doc Brown

명확히하기 위해 클래스 이름이 아닌 변수 이름이됩니다. 클래스 이름에 모든 기능을 사용하는 것은 좋지 않은 생각이라는 데 동의합니다. 그래서 나는 그런 workerThisWay것이 될 것을 제안하고 usingQuality있습니다. 작은 코드 스 니펫에서보다 구체적으로하는 것이 안전합니다. (그리고, 도메인에 "usingQuality"보다 더 좋은 문구가 있다면 그것을 사용하십시오.
— user949300

2

런타임 데이터에 따라 초기화가 달라지는 공통 인터페이스는 일반적으로 팩토리 패턴에 적합합니다.

DoThingsFactory factory = new DoThingsFactory(thingThatProvidesQuality);
DoSomething doSomething = factory.getDoer(info);

doSomething.letDoIt();

그런 다음 DoThingsFactory는 getDoer(info)콘크리트 DoThingsWithQuality 오브젝트 캐스트를 DoSomething 인터페이스로 리턴하기 전에 메소드 내부의 품질 정보를 가져오고 설정하는 것에 대해 걱정할 수 있습니다 .

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