2010-01-26 105 views
8

如何處理在java中只有單一繼承?這裏是我的具體的問題: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;} 

} 

這是我認爲我的最基本的實現,但我想讓其他實現。

可以說我想添加一個新變量say 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

是否'Length'必須實現它一個類而不是一個接口? – 2010-01-27 00:18:39

+0

@Loadmaster:長度是一個類,而不是一個接口,以便長度方法不必在Word和WordDescriptor中重複。對於這些方法來說,這些方法很簡單,但對於其他方面它們可能非常複雜。 – sixtyfootersdude 2010-01-27 03:59:50

+0

@Jake:如果其他事情可能非常複雜,您應該明確使用「策略模式」。 – 2010-01-27 16:25:01

回答

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

戰略模式定義了一個算法家族,封裝每個算法,並使它們可以互換。策略可以讓算法獨立於使用它的客戶端。

此解決方案不使用策略模式,但可以重構爲相同。

+0

+1哇,這是一個很棒的解決方案,不是非常直觀,但我認爲這應該工作得很乾淨。 – sixtyfootersdude 2010-01-27 04:03:22

+0

您的函數private WordLength calculateWordLength()不返回值,應該是void。 – 2010-01-27 04:15:30

+0

這是正確的crosvenir。我在記事本中寫了這個,所以沒有語法檢查。感謝您指出,我會更新它。 – 2010-01-27 14:31:05

2

與您的特定例如,你可以結合接口使用decorator模式(約缺乏明確的遺憾)以補充您的Word類與附加功能;例如

// *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中缺少多重繼承並不是真正的問題,更多的是重新設計你的設計。總的來說,過度使用繼承性被認爲是不好的做法,因爲深層繼承層次難以解釋/維護。

+0

WordExt具有屬性種類的兩個副本。第一個「objWordExt.kind」從AbstractWord繼承而來。第二個「objWordExt.word.kind」它從實例變量詞中獲得。 – 2010-01-27 00:37:42

+0

我考慮過使用類似的東西,但在這種情況下它不會真正起作用,因爲有些方法的長度很長且很複雜。使用裝飾器我需要在Word類和WordDescriptor類中實現它們。 (還是)感謝你的建議。 – sixtyfootersdude 2010-01-27 03:51:25

+0

@GB:好點。通常我會定義一個接口Word,WordExt會實現一個冗餘副本「kind」。 – Adamski 2010-01-27 09:06:15

0

(我不能僅僅因爲向後兼容性而將該字段添加到另一個字段,它就等於方法和類似的東西)。

它不會破壞源代碼兼容性。除非你在等號方法中做了一些非常瘋狂的事情。

重命名類通常不是處理二進制兼容性的方式。

+0

WordDescriptor的等號基於所有字段=>向後可比性。 – sixtyfootersdude 2010-01-27 14:24:42

+0

我想你會發現,如果兩個WordDescriptors在其他方面相同,它們將具有相同的字長。所以不,它不會破壞源代碼兼容性。 – 2010-01-27 20:04:35

3

只需使用組成,而不是繼承:

一個BetterWord是安AbstractWord具有-ALength

public class BetterWord extends AbstractWord { 

    private Length length; 

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

編輯

如果API需要一個長度類型的對象,只需添加一個吸氣劑:

public class BetterWord extends AbstractWord { 

    private Length length; 

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

    public Length getLength() { 
     return length 
    } 
} 

或重命名執行LengthLengthImpl和定義一個接口Length,因爲一個類可以實現多個接口。

+0

@Andreas,完全同意使用「has-a」與「is-a」,但我會認爲設置單詞的長度是不會針對特定單詞的對象生命週期進行修改的。 – 2010-01-26 23:12:35

+0

如果存在需要類型爲Length的對象的API,則這不起作用。請參閱下面的解決方案 – 2010-01-27 00:32:36

2

看着它,我的第一感覺是,你的模型是一個有點複雜。

一個單詞有一個字符串來描述該單詞本身被存儲在Word對象中,並與一個類一起說它是一個名詞,動詞,形容詞等。一個單詞的另一個屬性是存儲在該單詞中的字符串的長度字對象。在「是一個」術語的東西

思考和「具有-a」關係,你可以免去很多複雜的。

比如爲什麼你需要擴展AbstractWord一個WordDescriptor?一個詞是否會從動詞變成形容詞?我會認爲在創建對象時設置了單詞類型,並且在Word對象的生命週期中不會更改。一旦你有一個單詞「澳大利亞」的Word對象,這個單詞在對象的整個生命週期中都不會改變。

嗯。也許你可能有一個Word對象,在用「動詞」類型實例化對象來表示單詞「bark」來描述狗的聲音。然後你意識到你實際上需要讓Word對象表示描述樹的覆蓋的名詞。可能的,但狗的樹皮和樹的樹皮都可以存在。

所以我認爲你所選擇的模式是有點太複雜,你的問題可以回到和簡化原來的對象模型來解決。

開始問自己爲每種基本模型的繼承方面的問題。

當我說B類擴展A類時,我可以說B類是「A類」,而我正在專門化它的行爲嗎?

例如,基類動物可以擴展到提供袋鼠的專門類。然後你可以說「袋鼠」是一個「動物,你正在專注於這個行爲

然後看看屬性,袋鼠有一個位置屬性來描述它被發現的位置,然後你可以說一個袋鼠「有一個」的位置,袋鼠「是一個」的位置沒有意義

同樣,一個單詞「有一個」的長度,而一個單詞的說法是「一個長度只是沒有」沒有意義。

順便說一句澳大利亞在這篇文章中的參考文獻是故意慶祝今天是1月26日的澳大利亞日!在繼承

HTH

+0

祝你歡快的澳大利亞日。我同意你對WordDescriptor的評論。如果AbstractWord「有一個」字段(帶有getter和setter)的WordDescriptor將會更好。 – 2010-01-27 02:13:00

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

有了這個基本的設置,當你想介紹一個length屬性,所有你需要做的是這樣的:

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。

+0

感謝您的評論,但也許我不清楚WordDescriptor是什麼。單詞描述符沒有單詞。一個WordDescriptor可以描述很多單詞。例如,一個WordDescriptor =名詞可以指所有名詞。因此,如果一個WordDescriptor包含一個Word就沒有任何意義。 – sixtyfootersdude 2010-01-27 04:07:25

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的用戶會認爲

  • 哪些領域,我將需要fieldsA fieldsB fieldsC或另一個

所以成分不會在這裏工作 和你那可憐的嘗試都是沒用的

當ü想想Oject面向對象編程

你需要BIG模型6-7 lvls的多繼承

因爲這是一個很好的測試,因爲對應於現實生活或數學模型測試4000年文明的模型。

如果你的模型需要繼承停止的2個拉特使用OO假裝ü

U可以方便地與任何語言,甚至程序一個像C或Basic語言 */