Java中的多继承设计问题

时间:2010-01-26 22:37:21

标签: java inheritance multiple-inheritance

你如何处理在java中只有单一继承?这是我的具体问题:

我有三个(简化的)课程:

public abstract class AbstractWord{
    String kind;    // eg noun, verb, etc

    public String getKind(){ return kind; }

}

public class Word extends AbstractWord{
    public final String word;

    ctor...

    public void setKind(){
        // based on the variable word calculate kind..
    }
}

public class WordDescriptor extends AbstractWord{

    ctor..

    public void setKind(String kind){this.kind = kind;}

}

这是我认为最基本的实现,但我想做其他实现。

让我们说我想添加一个新的变量,例如wordLength,但我想用继承添加它。意思是我不想修改原始的AbstractWord类。也就是说:

public class Length{
    private int length;

    public int getLength(){return length};
}

public class BetterWord extends AbstractWord AND Length{

    public void setLength(){
        // based on the variable word calculate Length..
    }
}

public class BetterWordDescriptor extends AbstractWord AND length{

    public void setLength(int length){this.length = length;}
}

我知道java不允许我这样做,但它使我的代码非常难看。现在每当我添加一个字段时,我只是将它添加到AbstractWord,但后来我要么重命名AbstractWord(和Word和WordDescriptor)。 (我不能只是将字段添加到另一个字段,因为它具有向后兼容性,它会破坏等于方法和类似的东西)。

这似乎是一个非常常见的设计问题,但我已经绞尽脑汁,我无法想出任何漂亮的解决方案。

是否有解决此问题的设计模式?我有一些潜在的解决方案,但我想知道是否有一些我遗漏的东西。

感谢, 杰克

更新:长度是指单词中的音节数(抱歉缺乏清晰度)

7 个答案:

答案 0 :(得分:9)

赞成合成而不是继承。

解决方案考虑到可能存在可能需要WordLengthSupport的其他类型的单词。

类似地,可以创建和实现其他接口,并且各种单词类型可以混合和匹配这些接口。

public class WordLength {
    private int length = 0;
    public int getLength(){return length};
    public void setLength(int length){this.length = length};
}

public interface WordLengthSupport {
    public WordLength getWordLength();
}

public class BetterWord extends AbstractWord 
        implements WordLengthSupport {
    WordLength wordLength;
    public WordLength getWordLength() {
        if(wordLength==null) {
            // each time word changes 
            // make sure to set wordLength to null
            calculateWordLength(); 
        }
        return wordLength;
    }
    private void calculateWordLength() {
        // This method should be 
        //    called in constructor 
        //    or each time word changes 
        int length = // based on the variable word calculate Length..
        this.wordLength = new WordLength();
        this.wordLength.setLength(length);
    }
}

public class BetterWordDescriptor extends AbstractWord  
        implements WordLengthSupport {
    WordLength wordLength;
    public WordLength getWordLength(return wordLength);
    public void setWordLength(WordLength wordLength) {
        // Use this to populate WordLength of respective word
        this.wordLength = wordLength;
    }
}

战略模式定义了一系列算法,封装了每个算法,并使它们可以互换。策略允许算法独立于使用它的客户端。

此解决方案不使用策略模式,但可以对其进行重构。

答案 1 :(得分:3)

只使用合成而不是继承:

BetterWord AbstractWord 有一个 Length

public class BetterWord extends AbstractWord {

    private Length length;

    public void setLength(int value){
        length.setLength(value);
    }
}

修改

如果API需要Length类型的对象,只需添加一个getter:

public class BetterWord extends AbstractWord {

    private Length length;

    public void setLength(int value){
        length.setLength(value);
    }

    public Length getLength() {
        return length
    }
}

或者将实现Length重命名为LengthImpl并定义接口Length,因为类可以实现多个接口。

答案 2 :(得分:2)

通过您的具体示例,您可以将decorator模式与接口结合使用,以补充Word类的其他功能; e.g。

// *Optional* interface, useful if we wish to reference Words along with
// other classes that support the concept of "length".
public interface Length {
  int getLength();
}

// Decorator class that wraps another Word and provides additional
// functionality.  Could add any additional fields here too.
public class WordExt extends AbstractWord implements Length {
  private final Word word;

  public class(Word word) {
    this.word = word;
  }

  public int getLength() {
    return word.getKind().length();
  }
}

此外值得注意的是,Java中缺少多重继承并不是真正的问题;更多的是重新设计你的设计。一般来说,过度使用继承性被认为是不好的做法,因为深度继承层次结构难以解释/维护。

答案 3 :(得分:2)

看着它,我的第一感觉是你的模型有点复杂。

一个单词有一个String来描述存储在Word对象中的单词本身以及一个类,它说它是名词,动词,形容词等.Word的另一个属性是存储在Word中的字符串的长度对象

用“is-a”和“has-a”关系来思考问题,你可以消除很多复杂性。

例如,为什么你需要一个扩展AbstractWord的WordDescriptor?一个词会从动词转变为形容词吗?我原以为单词类型是在创建对象时设置的,并且在Word对象的生命周期内不会改变。一旦你有一个单词“澳大利亚”的Word对象,那么单词的种类在对象的生命周期内不会改变。

嗯。也许你可能有一个Word对象代表单词“bark”后用一种“动词”实例化对象来描述狗的声音。然后你意识到你实际上需要让Word对象来表示描述树的覆盖的名词。可能,但狗的树皮和树皮都可以存在。

因此,我认为您选择的模型有点过于复杂,您可以通过返回并简化原始对象模型来解决您的问题。

首先问自己一个关于基本模型的每个继承方面的问题。

  

当我说B级延伸A级时,我可以说B级“是 - A级”并且我专注于它的行为吗?

例如,可以扩展基类Animal以提供专门的袋鼠类。然后你可以说“袋鼠”是一个“动物。你是专门的行为。

然后查看属性,Kangaroo有一个Location属性来描述它的位置。然后你可以说一个袋鼠“有一个”的位置。袋鼠“is-a”的位置没有意义。

同样,Word“有一个”长度。一句话“是一个”长度的陈述是没有意义的。

BTW本文中所有澳大利亚人的参考文献都是为了庆祝澳大利亚日,即今天的1月26日!

HTH

答案 4 :(得分:0)

  

(由于向后兼容性,我不能只将字段添加到另一个字段,它会破坏等于方法和类似的东西。)

它不会破坏源兼容性。除非你在你的平等方法中做一些非常疯狂的事情。

重命名类通常不是处理二进制兼容性的方法。

答案 5 :(得分:0)

问题不在于“如何处理单一继承”。你所缺少的并不是一种设计模式,而是学习如何与实现分开设计API。

我会像这样实现它:

public interface WordDescriptor {
    void getKind();
    Word getWord();
}

public interface Word {
    String getWord();
}

public class SimpleWord implements Word {
    private String word;

    public SimpleWord(String word) { this.word = word; }
    public String getWord() { return word; }
}

public class SimpleWordDescriptor implements WordDescriptor {
    private Word word;
    private String kind;

    public SimpleWordDescriptor(Word word, String kind) {
        this.word = word;
        this.kind = kind; // even better if WordDescriptor can figure it out internally
    }

    public Word getWord() { return word; }

    public String getKind() { return kind; }
}

通过这个基本设置,当你想引入一个长度属性时,你所要做的就是:

public interface LengthDescriptor {
    int getLength();
}

public class BetterWordDescriptor extends SimpleWordDescriptor
                                  implements LengthDescriptor {
    public BetterWordDescriptor(Word word, String kind) {
        super(word, kind);
    }

    public int getLength() { getWord().length(); }        
}

使用属性组合以及Decorator模式的其他答案也是完全有效的问题解决方案。您只需要确定您的对象是什么以及它们是如何“可组合”的,以及如何使用它们 - 因此首先要设计API。

答案 6 :(得分:0)

/ **  *第一个例子  * /

class FieldsOfClassA {
    public int field1;
    public char field2;
}

interface IClassA {
    public FieldsOfClassA getFieldsA();
}

class CClassA implements IClassA {
    private FieldsOfClassA fields;

    @Override
    public FieldsOfClassA getFieldsA() {
        return fields;
    }
}

/**
 * seems ok for now
 * but let's inherit this sht
 */


class FieldsOfClassB {

    public int field3;
    public char field4;
}

interface IClassB extends IClassA {

    public FieldsOfClassA getFieldsA();
    public FieldsOfClassB getFieldsB();
}

class CClassB implements IClassB {

    private FieldsOfClassA fieldsA;
    private FieldsOfClassB fieldsB;

    @Override
    public FieldsOfClassA getFieldsA() {
        return fieldsA;
    }

    @Override
    public FieldsOfClassB getFieldsB() {
        return fieldsB;
    }
}

/ **

  • 哇这怪物变大了

  • 想象一下,你需要4个lvl的继承

  • 写这个地狱需要很多时间

  • 我甚至不会说那些iface的用户会想到

  • 我需要哪些字段fieldA fieldsB fieldsC或另一个

所以这里的构图不起作用 而你的可怜尝试是没用的

当你想到面向Oject的编程时

你需要具有6-7 lvls多重继承的BIG模型

因为这是一个很好的测试,因为它对应于现实生活模型或数千年文明测试模型。

如果您的模型需要2 lvl继承,请停止假装使用OO

U可以使用任何语言轻松实现它,甚至是C语言或基本语言等程序语言  * /