2009-12-10 9 views
10

문제는 이것이다 호출 구체적인 클래스는 사용자 정의 할 필요가 있고 하나 개의 솔루션은 구체적인 클래스를 확장하는 것입니다점프 조부모의

class CustomizedClass1 extends ConcreteClass1 { 
    CustomizedCLass1(){ super(); /* useful implementation */ } 
} 

그러나 문제는 사용자 정의 된 클래스는 추상 클래스의 생성자가 아닌 구체적인 클래스의 생성자를 호출하기 만한다는 것입니다.

어떻게 이것을 수행합니까? 수업 관계를 변경하기위한 제안은 유효합니다.

EDIT : 구체적인 예는 ConcreteClass1과 CustomizedClass1이 서로 다른 데이터 세트 (ConcreteData1 및 CustomizedData1)를 가지며 클래스 생성자의 데이터베이스에서 검색된다는 것입니다. 문제는 CustomizedClass1의 인스턴스를 만들면 두 데이터 엔터티를 모두 가져옵니다.

저는 단순한 상속을 사용하는 것이 최선의 방법이 아니라는 것을 알고 있습니다. 그래서 나는 클래스의 관계를 변경하라는 제안이 유효하다는 것을 지적했습니다.

+7

'ConcreteClass1' 생성자를 호출하지 않으면'ConcreteClass1 '이 아닙니다. –

+0

몇 가지 해답이 있지만 Java와 OO에서는 매우 부자연 스럽습니다. 최선의 해답은 객체 디자인에 도전하고 is-A 및 has-A 관계에 대해 생각하도록 권장합니다. –

+0

그러나 개발의 속도는 때때로 중요 할 수도 있습니다. 그렇죠? – mlvljr

답변

9

쉬운 (왜?) :

class AbstractClass { 
    AbstractClass(){ /* useful implementation */ } 
} 

class ConcreteClass1 extends AbstractClass { 
    ConcreteClass1(){ super(); /* useful implementation */ } 
    ConcreteClass1(boolean skip){ super(); } 
} 
class CustomizedClass1 extends ConcreteClass1 { 
    CustomizedCLass1(){ super(true); /* useful implementation */ } 
} 
+0

그런 생각은 한 번도 ...하지만 나는 "왜?"라고 묻습니다. 질문 :-) – TofuBeer

+0

내 자선 추측은 ConcreteClass1에 있습니다. 그는 외부 리소스의 초기화를 수행 할 수 있으며 CustomizedClass1에서는 다른 외부 리소스를 초기화하려고합니다. – MattMcKnight

+1

이 경우라면 그는 부모의 형제 인 클래스를 만들거나 ConcreteClass1을 두 개의 다른 클래스로 리팩터링해야 할 것입니다. –

21

Java에서는이 작업을 수행 할 수 없습니다. 나는 종종이 일을하고 싶어하는 학생들이 있지만, 그들이 정말로하고 싶은 일을 한 번도 본 적이 없다. 들어

:

당신은 당신이하고 싶은 이유 (당신의 설명이 너무 모호) 나는이 솔루션은

편집 :-) 달성 할 수있다 확신 무엇인지 구체적인 예를 줄 수 인간의 생성자는 Mammal 생성자 건너 뛸 수 있다면 당신은 인간과 바람 것

Animal (constructor makes the eyes) 
    | 
Mammal (constructor makes the lungs) 
    | 
Human (constructor sets the language) 

사람이 더있다 :이 작업을 수행하지 않으려는 이유의 실제 예는 (일반적으로) 계층 구조처럼이 없을 것 폐 ... 아주 유용하지는 않습니다.

+11

동물 <-Mammal <-Aquaman : 포유류의 폐 건설을 제외한 모든 것을 상속받습니다. , 그리고 자신의 아가미를 구현합니다.;) – erickson

+1

다음 Mammal의 생성자는 생성자에 대한 인수로 "호흡 메커니즘"을 가져 와서 폐로 사용 된 인스턴스 변수에 할당해야합니다. – TofuBeer

+1

이 누락 된 점은 Mammal 생성자가 반드시 멱등수는 아니며 하위 클래스에서 동일한 생성자를 항상 호출하고 싶지는 않습니다. 적어도 하나 이상의 생성자를 호출해야하지만 모든 생성자가 동일한 생성자를 필요로하지는 않습니다. – MattMcKnight

8

CustomizedClass1의 모든 인스턴스는 정의에 의해, 또한 ConcreteClass1의 인스턴스이므로 CustomizedClass1 생성자가 실행하기 전에 유효한 ConcreteClass1 인스턴스로 구성해야합니다. 그렇지 않은 경우 ConcreteClass1 메서드를 호출하면 어떻게됩니까? 그들은 아직 초기화되지 않은 변수를 조작하려고합니다.

이렇게해야 할 필요가 있다고 판단되는 경우 디자인을 다시 생각해 볼 기회가 있습니다. 예를 들어 ConcreteClass1의 일부 기능 만 원할 경우 해당 기능을 ConcreteClass1의 수퍼 클래스로 분해 할 수 있으며 CustomizedClass1을 확장하여 필요한 기능을 얻을 수 있습니다.

이러한 클래스 간의 관계에 대해 자세히 알려주십시오.

1

이것은 Java가 처리 할 수있는 기능이 충분하지 않은 것 같이 내게 들립니다.

희망하시는 대답이 아니거나 입력하는 것을 자랑 스럽지만 ConcreteClass1을 모방 한 ConcreteClass2을 만들고 AbstractClass의 생성자를 사용할 수 있습니다.

@TofuBeer가 말했듯이, 이것은 Java가 지원하는 것이 아닙니다. 이러한 이유로 일부 현대 언어 (예 : Scala가있는 Traits)가 열정적 인 개발자가되었습니다.

0

새로 만든 ConcreteClass1 인스턴스가 AbstractClass 인스턴스처럼 동작하도록 사용자 정의하지 않는 이유는 무엇입니까? (ConcreteClass1에 해당하는 보호 된 메소드가있는 경우에만) 즉 다음과 같이

class AbstractClass { 
    public AbstractClass() { /* useful implementation */ } 
} 

class ConcreteClass1 extends AbstractClass { 
    public ConcreteClass1() { 
     /* A hidden super() call put here by the compiler. */ 
     /* Useful implementation */ 
    } 

    protected void customize_with(SomeParams customization_params) { 
     /* 
     Customize this ConcreteClass1 instance according to parameters 
     passed. As a result, the instance behavior will 'revert' (in the 
     way you need it) to that of an AbstractClass instance. 
     */ 
    } 
} 

class CustomizedClass1 extends ConcreteClass1 { 
    public CustomizedCLass1() { 
     /* A hidden super() call put here by the compiler. */ 
     customize_with(customization_params); 
     /* Rest of useful implementation */ 
    } 
} 

설계 의도와 여기에 논리가있을 수 있습니다 :

  1. 당신은 상속을 통해 ConcreteClass1의 (기본) 동작을 얻으려면, 당신은이 코스을 강요하며의 (그것으로부터 상속 에서 상속 가치가있는 디자인).

  2. 기본적으로 ConcreteClass1이 제공하는 동작을 사용자 정의하고 싶습니다. 달성하고자하는 사용자 정의는 일반적으로 일부 매개 변수로 설명 될 수 있습니다. 이 매개 변수를 보호 할 수있는 CustomizedClass1의 특수 메서드에 전달하고 그에 따라 customize() 이름을 지정하기 만하면됩니다.

  3. ConcreteClass1() 생성자에서 수행되는 사용자 정의는 구체적으로 클래스 인스턴스의 동작을 AbstractClass의 '되돌리기'로 설정할 수 있습니다. 실제로이 경우, ConcreteClass1()에서 수행 실제 물건에 따라 약간의 오버 헤드를 초래할 수있다 customize_with()를 호출

  4. 은 (동적으로 사용자 정의 할 수 ConcreteClass1 '인스턴스를하고자하지 않는 생성자는 확실히 더 나은 솔루션입니다 과부하 (및 보호)를 사용하여 이 경우 customize_with() 및 나머지 부분은 ConcreteClass1이므로 계약에 따라 이러한 동작을 지원해야합니다.

구문에 문제가 있으면 실례합니다 (Java 코드를 많이 작성하지 않았습니다).

+0

질문은 ConcreteClass1의/* 유용한 구현 */부분 실행을 피하는 것이 었습니다.이 솔루션이이를 달성했다고 생각하지 않습니다. –

+0

@Kenneth Xu 가능성이 있지만, 노골적인 노하우에 관한 질문조차도 더 나은 실천 중심의 답변을받을 가치가 있습니다! 즉,이 솔루션은 더 나은 것을 달성하려고 시도합니다 (또는 적어도 나는 그렇게 생각합니다). Cheeers! – mlvljr

7

두 의견 : 첫째, 당신은이 같은 생성자 '위로 점프'의 관점에서 생각 안하고 있습니다. 둘째, 은 실제로처럼 들리지만 수업 관계를 재고해야합니다.

"A가 B를 확장한다고 생각할 때마다 ..."는 상황을 자세히 살펴볼 수있는 아주 좋은 시간입니다. 'Extends'는 'a is a'를 의미합니다.이 관계는 임의의 관계입니다. 선택적인 동작을 사용하면 회색 영역이 추가되어 나중에 물게됩니다.

사람들이 말했듯이 ConcreteClass1에 여러 생성자를 제공하여 각각의 경우에 필요한 초기화를 수행 할 수 있습니다. 즉, 하위 클래스에서만 사용할 수 있도록 보호 할 수 있습니다. 그러나 여기에 질문이 있습니다. 누군가가 ConcreteClass1의 일부 기능 (전부는 아님)을 필요로하는 CustomizedClass2를 작성하려면 어떻게해야합니까? 다른 사용자 정의 생성자를 추가합니까?

+1

우리가 여기에서 말하고있는 전부는 초기화 기능을 생성자에서 건너 뛰는 것입니다. "일부 기능 (전부는 아니지만)"이 아닙니다. 그것은 LSP 위반 일 것입니다. :-) – MattMcKnight

+0

예제에서 ConcreteClass1을 'swiss-knife'사용자 정의 가능한 클래스 (자체 상속 및 인스턴스화 모두 가능)로 미리 디자인 (및보기)하는 것이 좋습니다. 따라서 필요한 기능 (또는 요구 사항이 변경됨)을 정말로 잊어 버렸고 정확하게 ConcreteClass1 클래스를 CustomizedClass2의 기반으로 사용해야하는 경우 - 적절한 메소드 (생성자 포함) 추가 또는 기존 클래스 맞춤 설정. 이는 주제 시동기 디자인의 근거가 될 수 있습니다. – mlvljr

0

한 가지 방법은 내가 생각 해낸 : 이런 식으로

class AbstractClass { 
    AbstractClass(){ init(); } 
    protected init(){ /* useful implementation */ } 
} 

class ConcreteClass1 extends AbstractClass { 
    ConcreteClass1(){ init(); /* useful implementation */ } 
} 

class CustomizedClass1 extends ConcreteClass1 { 
    CustomizedCLass1(){ init(); /* useful implementation */ } 
} 

, CustomizedClass1는 ConcreteClass1 초기화를 통과하지 않고는 AbstractClass에서 필요로하는 동작을 가져옵니다.

편집 : 부모의 생성자가 암시 적으로 하나의 의견 제시 자로 지적 되었기 때문에 부적절합니다. 쉬운 방법은 다른 생성자를 만드는 것입니다.

+2

이것은 작동합니다. 나는 여기에 대한 많은 답이 미쳤다고 생각한다. 여러 생성자를 사용함으로써 내가 제안한 방식을 사용할 수 있다고 생각합니다. 기본 초기화를 실행하지 않고 객체를 생성하려는 경우가 종종 있습니다. 가장 높은 평가를받은 포스터가 그것이 어떤 종류의 범죄라고 생각하는 이유를 이해하지 못합니다. – MattMcKnight

+0

* 미친 * 이름을 지어주세요. :))) – mlvljr

+0

@ mlvjr 너의 것이 미치지 않았다. 실제로이 대답에 꽤 가깝다. 미친 것들은 그의 질문을 명확하게 묻지 않기 위해이 사람을 우묵하게 데려가 "강한"객체 지향 지식을 보여줄 수 있습니다. – MattMcKnight

1

CustomizedData1은 ConcreteData1의 하위 클래스입니까? 만약 그렇다면, 나는 ConcreteClass1을위한 (아마도 보호 된) 생성자를 제안 할 것이다. ConcreteClass1은 초기화 중에 자신을 가져 오는 대신 ConcreteData1을 사용한다. 이렇게하면 CustomizedClass1이 CustomizedData1을 가져 와서 super에 대한 호출에 전달할 수 있습니다. 불행히도 이것은 내부 init 전에 데이터를 가져올 수 없다면 까다 롭거나 상당히 불가능할 수 있습니다.


class ConcreteClass1 extends AbstractClass { 
    ConcreteClass1(){ 
      this(...fetch data as before...); 
    } 
    ConcreteClass1(ConcreteData1 data){ 
      myData = data; 
      ... 
    } 
} 
class CustomizedClass1 extends ConcreteClass1 { 
    CustomizedCLass1(){ 
      super(new CustomizedData1(...)); 
      ... 
    } 
} 

그러나 CustomizedClass1은 데이터를 단순히 ConcreteData1이 아닌 CustomizedData1로 참조해야 할 수 있습니다. 그것은 상속 된 ConcreteData1을 항상 타입 변환 할 수 있지만, 그것은 대단한 것 같습니다. 그러나 데이터에 자체 참조를 저장하는 경우 최종 레코드가 아닌 경우 참조를 동기화 상태로 유지해야합니다.

0

네, 조정할 수 있습니다! 부모 클래스 (B)에 다른 메소드를 추가하고 자식 클래스 (C)에 super.executeParentA(); 하나 (실제로)이 작업을 수행 할 수 있습니다 곳 메소드 호출 super.execute()에

A --> execute() 

B --> executeParent_A() { super.execute(); } 

C --> super.executeParent_A(); 

-Mehboob

0

나는 두 가지 경우를 생각할 수 :

경우 1, ConcreteClass2 공유 실행 처음 클래스를 초기화하지만, ConcreteClass1과 다른/충돌하는 자체 초기화 시퀀스 -> init() 메소드를 가지고 그것을 대체합니다 (ConcreteClass1의 생성자를 오버라이드하려고 시도하는 대신). 이러한 경우에

public class ConcreteClass1 extend AbstractClass 
{ 
    protected F1 f; 

    public ConcreteClass1() { 
    super(); 
    this.f = new F1(); 
    } 
} 

public ConcreteClass2 extends ConcreteClass1 
{ 
    public ConcreteClass2() { 
    super(); // I'd like to do super.super() instead (no, you don't) 
    this.f = new F2(); // We wasted time with new F1(); 
    } 
} 

, 초기화() 방법을 사용 중 하나

사례 2, 당신은 (이것은 실제로 이전의 특정 사건) 하나 (또는 ​​그 이상) 클래스 멤버의 다형성 초기화가 또는이 작업을 수행 :

protected ConcreteClass1 (F1 f) { 
    super(); 
    this.f = f; 
} 
public ConcreteClass1() { 
    this (new F1()); 
} 

... 

public ConcreteClass2() { 
    super (new F2()); 
} 

을 즉, 암시 적으로 금지되어 계층 구조 위에 점프과 좋은 이유는 다른 답변에서 설명을 위해, 명시 적 점프에 대한 이유를 확인하십시오.