2009-12-10 56 views
10

问题是这样的:我有一个抽象的类,它在其构造一些工作,一组实现抽象类的子类:跃过父类的构造调用祖父母的

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

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

然后,具体类需要进行定制和一种解决方案是为了延长水泥类:

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

但问题是,定制类只需要调用抽象类的构造函数,而不是具体的类的构造函数。

你是如何做到这一点的?改变班级关系的建议是有效的。

编辑:具体的例子是ConcreteClass1和CustomizedClass1具有不同的数据集(ConcreteData1和CustomizedData1),它从类的构造函数中从数据库中检索。问题是创建CustomizedClass1的实例将检索两个数据实体。

我知道,使用简单的继承可能不是最好的事情,这就是为什么我指出改变类的关系的建议是有效的。

+7

如果您不调用ConcreteClass1构造函数,它不是'ConcreteClass1'。 – 2009-12-10 04:06:33

+0

几个答案显示了一些技巧来做到这一点,但这在Java和OO中非常不自然。最好的答案会挑战你的对象设计,并鼓励你用is-A和has-A关系来思考。 – 2009-12-20 20:51:55

+0

但是发展速度有时也很重要,不是吗? – mlvljr 2009-12-21 17:49:13

回答

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 2009-12-10 05:18:02

+0

我的慈善猜测是在ConcreteClass1他可以做一些外部资源的初始化,并在CustomizedClass1他希望不同的外部资源初始化。 – MattMcKnight 2009-12-10 05:30:05

+1

如果是这种情况,那么他可能需要创建一个父类的兄弟类,或者将ConcreteClass1重构为两个不同的类。 – 2009-12-10 17:46:19

21

你不能用Java来做到这一点。我经常有学生想要这样做,但我从来没有见过这样的情况,他们真的想做什么。

你能给的是什么,你想要做的和为什么(你的描述过于模糊),我相信一个解决方案可以:-)

编辑来实现一个具体的例子:

对于为什么你不希望这样做一个真实的例子(一般)将是一个层次,如:

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

如果人体构造可以跳过比哺乳动物的构造函数,那么你会风与一个人谁没有肺...不是很有用。

+11

动物<-Mammal <-Aquaman:继承一切,但哺乳动物的肺结构,并实施自己的鳃。;) – erickson 2009-12-10 06:49:10

+1

然后哺乳动物的构造函数应该将“呼吸机制”作为构造函数的参数,并将其赋值给曾经是肺的实例变量:-) – TofuBeer 2009-12-10 07:24:06

+1

我认为缺少的一点是Mammal构造函数是不一定是幂等的,你并不总是想从子类中调用相同的构造函数。您仍然必须调用至少一个构造函数,但不是每个人都需要相同的构造函数。 – MattMcKnight 2009-12-10 19:20:52

8

CustomizedClass1任何实例也是ConcreteClass1一个实例,根据定义,所以它必须被构造为有效ConcreteClass1实例之前的CustomizedClass1构造可以运行。否则,如果你调用ConcreteClass1方法会发生什么?他们会尝试对尚未初始化的变量进行操作。

如果您认为您需要这样做,那么很可能您的设计需要重新思考。例如,如果您只需要ConcreteClass1中的某些功能,则可以将该功能分解为ConcreteClass1的超类,而CustomizedClass1可以扩展该功能以获得所需的功能。

请提供有关这些类之间关系的更多信息。

1

这对我来说听起来像是一个混合的问题 - Java没有很好的处理能力。

虽然它不是您希望的答案,或者我很自豪能够输入的答案,但您可以简单地创建ConcreteClass2,模拟ConcreteClass1并使用AbstractClass的构造函数。

正如@TofuBeer所说,这不是Java支持的东西。这就是为什么一些现代语言(即斯卡拉w /特质)正在获得充满激情的开发人员。

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,你可能会问了(如果我得到它的权利)。

  4. 调用customize_with()实际上可能会引入一些开销取决于ConcreteClass1()做实际的东西,在这种情况下使用重载的(也可能是受保护的)构造绝对是一个更好的解决办法,除非你想ConcreteClass1“实例是动态定制(在这种情况下,customize_with()和其他ConcreteClass1应该相应地设计,即通过合同来支持这种行为)。

对不起,如果语法有任何问题(我没有写太多的Java代码)。

+0

问题是为了避免执行ConcreteClass1的/ *有用的实现* /部分,我不认为这个解决方案实现了它。 – 2011-07-23 21:39:32

+0

@Kenneth Xu可能,但是,即使是关于如何做的骇客问题,也应该有更好的实践导向的答案!换句话说,这个解决方案试图实现更好的东西(或者至少我认为是这样:))。 Cheeers! – mlvljr 2011-07-23 22:44:45

7

两点意见: 首先,你不应该去思考的“跳过”这样的构造条件。 其次,它真的听起来像你需要重新考虑你的班级关系。

任何时候当你发现自己在想“A扩展B,除了...”是进一步观察事物的好时机。 '延伸'意味着'是',这是一种或者说关系:具有可选行为会增加灰色区域,这些灰色区域会在稍后出现。

正如人们所说的,您可以在ConcreteClass1上提供多个构造函数,以在每种情况下执行所需的初始化操作,也许可以使其受到保护,以便它们只能由子类使用。但是这里有一个问题:如果有人想编写CustomClass2需要ConcreteClass1中的一些(但不是全部)功能呢?你添加另一个自定义构造函数吗?

+1

我们在这里谈论的是跳过构造函数中的任何初始化,而不是“某些(但不是全部)的功能”。这将是一个LSP违规。 :-) – MattMcKnight 2009-12-10 06:14:35

+0

在您的示例中,可以事先将ConcreteClass1设计(和查看)为'swiss-knife'可定制类(可以自己继承和实例化),这可能会更好。所以是的,如果你真的忘了需要的功能(或者需求已经改变),并且绝对需要使用ConcreteClass1类作为CustomizedClass2的基础 - 添加适当的方法(包括构造函数)或定制现有的方法。这可能是主题创始人设计背后的基本原理。 – mlvljr 2009-12-10 06:22:46

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初始化。

编辑:哎呀,这不起作用,因为父母的构造函数被隐式地称为评论者之一指出,我认为简单的方法是有不同的构造函数。

+2

这起作用。我认为这里的很多答案都很疯狂。我想你可以用我建议的方式,通过使用多个构造函数。通常情况下,您希望在不运行默认初始化的情况下创建对象。我不明白为什么最高评分的海报认为这是某种犯罪。 – MattMcKnight 2009-12-16 14:19:08

+0

你能说出这个*疯狂的*吗? :)) – mlvljr 2009-12-17 06:10:02

+0

@mlvjr你的不疯狂......它实际上非常接近这个答案。疯狂的是那些让这个人不要清楚地问他的问题,以便他们可以展示他们“强大”的面向对象的理解。 – MattMcKnight 2009-12-17 16:07:44

1

是CustomizedData1 ConcreteData1的子类吗?如果是这样,那么我会建议拥有ConcreteClass1的一个(可能受保护的)构造函数,该构造函数接受ConcreteData1而不是在初始化期间获取它自己的属性。通过这种方式,CustomizedClass1可以获取其CustomizedData1并将其传递给super。不幸的是,如果在某些内部初始化之前无法获取数据,这可能会非常棘手或者相当不可能。


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

但随后CustomizedClass1可能需要作为CustomizedData1不仅仅是ConcreteData1到数据的引用。它可能只是一直对其继承的ConcreteData1进行类型转换,但这看起来很糟糕。但是,如果它存储自己对数据的引用,那么如果它们不是最终的,则需要保持引用同步。

0

是的,你可以调整!将另一个方法添加到父类(B),并在您的Child类(C)中将其称为super.executeParentA();在这个方法调用super.execute()

A --> execute() 

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

C --> super.executeParent_A(); 

-Mehboob

0

我能想到的两种情况下,人们可能会想这样做(其实不是):

案例1,ConcreteClass2运行共享然后执行自己的初始化序列,它与ConcreteClass1中的不同/冲突 - 具有init()方法并覆盖它(而不是试图覆盖ConcreteClass1的构造函数)。

案例2,你有一个(或多个)类成员的多态初始化(其实这是前一个的具体情况):

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(); 
    } 
} 

在这种情况下,无论是使用init()方法,或做到这一点:

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

... 

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

换句话说,做出跳跃明确的原因,在层次结构是被禁止的隐式跳跃,为很好的理由在其他的答案解释。