2010-01-26 2 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). (Я не могу просто добавить поле к другому из-за обратной совместимости, он прерывает одинаковые методы и тому подобное).

Это похоже на довольно распространенную проблему с дизайном, но у меня возникла проблема, и я не могу придумать какие-либо красивые решения.

Есть ли шаблон дизайна, который обращается к этому? У меня есть некоторые потенциальные решения, но я хотел посмотреть, нет ли чего-то, чего я не видел.

спасибо, Jake

Update: Длина относится к числу слогов в слове (пардон об отсутствии ясности)

+0

ли 'Length' должен быть класс вместо интерфейса? –

+0

@Loadmaster: Length - это класс, а не интерфейс, поэтому методы в длину не должны дублироваться как в Word, так и в WordDescriptor. Для длины эти методы являются простыми, но для других вещей они могут быть очень сложными. – sixtyfootersdude

+0

@ Jake: Если другие вещи могут быть очень сложными, вы должны определенно использовать «Шаблон стратегии». –

ответ

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

+0

Ваша функция, закрытая WordLength calculateWordLength(), не возвращает значение и должна быть недействительной. –

+0

Это правильно кроссвенир. Я написал это в блокноте, поэтому проверки синтаксиса не было. Спасибо за указание, я обновлю его. –

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», который он получает из переменной переменной word. –

+0

Я подумал об использовании чего-то подобного, но в этом случае это не будет работать, потому что некоторые методы в длине довольно длинные и сложные. Используя декоратор, мне нужно будет реализовать их как в классе Word, так и в классе WordDescriptor. Спасибо за предложение. – sixtyfootersdude

+0

@GB: Хорошая точка. Обычно я определяю интерфейс Word, который WordExt будет реализовывать, чтобы обзавестись избыточной копией «kind». – Adamski

0

(Я не могу просто добавить поле к другому из-за обратной совместимости, он прерывает методы равенства и т. Д.).

Он не нарушит совместимость источника. Нет, если вы не делаете что-то действительно сумасшедшее в своих методах равных.

И переименование классов обычно не является способом обработки двоичной совместимости.

+0

Значения для словаря WordDescriptor основаны на всех полях => сломанной обратной сопоставимости. – sixtyfootersdude

+0

Я думаю, вы обнаружите, что если два WordDescriptor в противном случае равны, они будут иметь ту же длину слова независимо. Таким образом, нет, это не нарушает совместимость источника. –

3

Просто используйте состав вместо наследования:

BetterWordявляется-анAbstractWord, что имеет-аLength:

public class BetterWord extends AbstractWord { 

    private Length length; 

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

EDIT

Если API нужен объект типа Length, просто добавьте геттер:

public class BetterWord extends AbstractWord { 

    private Length length; 

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

    public Length getLength() { 
     return length 
    } 
} 

Или переименовать реализацию Length в LengthImpl и определить интерфейс Length, так как класс может реализовать несколько интерфейсов.

+0

@Andreas, полностью согласен с использованием «has-a» по сравнению с «is-a», но я бы подумал, что установка длины слова была чем-то, что не было бы изменено для жизни объекта определенного слова. –

+0

Если существует API, которому нужен объект типа Length, это не сработает. См. Мое решение ниже. –

2

Глядя на это, мое первое ощущение заключается в том, что ваша модель немного сложна.

Слово имеет строку, описывающее само слово, хранящееся в объекте Word, вместе с классом, чтобы сказать, что это существительное, глагол, прилагательное и т. Д. Другим свойством Word является длина строки, хранящейся в Word.

Подумайте о вещах с точки зрения отношений «is-a» и «has-a», и вы можете удалить много сложностей.

Например, почему вам нужен WordDescriptor, который расширяет AbstractWord? Будет ли слово изменяться от глагола к прилагательному? Я бы подумал, что тип слова был задан, когда объект был создан и не изменился во время жизни объекта Word. Как только у вас появился объект Word для слова «Австралия», тип слова не изменился бы на время жизни объекта.

Хммм. Возможно, у вас может быть объект Word, представляющий слово «кору» после создания объекта с типом «глагола», чтобы описать звук, создаваемый собакой. Затем вы понимаете, что вам действительно нужно, чтобы объект Word представлял существительное, которое описывает покрытие дерева. Возможно, но и кора собаки, и кора дерева могут существовать.

Итак, я думаю, что выбранная вами модель слишком сложна и ваш вопрос можно решить, возвращаясь и упрощая исходную объектную модель.

Начните с вопроса о каждом аспекте наследования вашей базовой модели.

Когда я говорю, что класс B расширяет класс A, могу ли я сказать, что класс B является «классом A» и что я специализируюсь на его поведении?

Например, базовый класс Animal может быть расширен, чтобы обеспечить специализированный класс кенгуру. Тогда вы можете сказать, что «кенгуру» - это «животное». Вы специализируетесь на поведении.

Затем посмотрите на атрибуты, у Kangaroo есть атрибут Location, чтобы описать, где он находится. Тогда вы можете сказать, что кенгуру «has-a» location. «Kangaroo« is-a »не имеет смысла.

Аналогично, слово« имеет-длину », а выражение« есть »- это« длина просто не делает », Имеют смысл.

BTW Все австралийские ссылки в этом сообщении предназначены для празднования Дня Австралии, который сегодня 26 января!

НТН

+0

Желаю вам радостного дня в Австралии. Я согласен с вашими комментариями на WordDescriptor. Лучше, если AbstractWord «has-a» (с геттером и сеттером) WordDescriptor. –

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.

+0

Спасибо за комментарий, может быть, я не понял, что такое WordDescriptor. Дескриптор слова не имеет ни слова. Один WordDescriptor может описывать многие слова. Например, WordDescriptor = существительное может ссылаться на все существительные. Поэтому не имеет смысла, если WordDescriptor содержит Word. – sixtyfootersdude

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 лвл наследования

  • это заняло бы слишком много времени, чтобы написать этот ад

  • Я даже не говорю, что пользователь этих IFACE будет думать

  • какие поля я потребуется fieldsB fieldsC полей А или другой

Так композиция не работает здесь и ваши жалкие попытки бесполезны

Когда думают о роекте ориентированного программировании

u нужны БОЛЬШИЕ модели с 6-7 lvls множественного наследования

потому что это хороший тест и потому, что соответствует моделям реальной жизни или математическим моделям, проверенным цивилизацией на 4 тысячи лет.

Если ваши модели требуют 2 лвли наследственность остановки, делая вид U используя OO

U можно легко реализовать с любым языком даже процедурным как C или Basic языка */

Смежные вопросы