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


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

에 대한 qualityletDoIt()호출 과 함께 설정해야 한다고 가정합니다 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.