Java中的多重inheritance设计问题

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

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

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但我想用inheritance添加它。 意思是我不想修改原始的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)。 (我不能只是因为向后兼容性而将字段添加到另一个字段,它会破坏等于方法和类似的东西)。

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

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

谢谢,杰克

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

赞成合成而不是inheritance。

解决方案考虑到可能存在可能需要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; } } 

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

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

只需使用组合而不是inheritance:

一个BetterWord 是一个 具有 Length AbstractWord

 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 ,因为类可以实现多个接口。

通过您的具体示例,您可以将装饰器模式与接口结合使用,以补充Word类的其他function; 例如

 // *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中缺少多重inheritance性并不是真正的问题; 它更像是重新设计你的设计。 一般而言,过度使用inheritance性被认为是不好的做法,因为深度inheritance层次结构难以解释/维护。

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

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

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

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

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

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

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

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

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

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

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

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

HTH

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

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

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

问题不在于“如何处理单一inheritance”。 你所缺少的并不是一种设计模式,而是学习如何与实现分开设计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。

/ ** *第一个例子* /

 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的inheritance

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

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

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

所以组合在这里不起作用,你的可怜尝试是无用的

当你想到面向Oject的编程时

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

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

如果你的模型需要2 lvl的inheritance,请停止使用OO假装你

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