你如何处理在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)。 (我不能只是将字段添加到另一个字段,因为它具有向后兼容性,它会破坏等于方法和类似的东西)。
这似乎是一个非常常见的设计问题,但我已经绞尽脑汁,我无法想出任何漂亮的解决方案。
是否有解决此问题的设计模式?我有一些潜在的解决方案,但我想知道是否有一些我遗漏的东西。
感谢, 杰克
更新:长度是指单词中的音节数(抱歉缺乏清晰度)
答案 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语言或基本语言等程序语言 * /