views:

223

answers:

6

How do you deal with having only single inheritance in java? Here is my specific problem:

I have three (simplified) classes:

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

}

This is what I consider my most basic implementation but I want to make other implementations.

Lets say that I want to add a new variable say wordLength but I want to add it using inheritance. Meaning I do NOT want modify that original AbstractWord class. Ie something along the lines of this:

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

I know that java does not let me do this but it has made my code very ugly. Right now whenever I add a field I am just adding it to AbstractWord but then I either need to rename that AbstractWord (and Word and WordDescriptor). (I can't just add the field to the other one because of backwards compatibility, it break equals methods and stuff like that).

This seems like a pretty common design issue but I have racked my head and I cannot come up with any beautiful solutions.

Is there a design pattern that addresses this? I have some potential solutions but I wanted to see if there was something that I was missing.

thanks, Jake

Update: Length refers to the number of syllables in the word (sorry about the lack of clarity)

+2  A: 

With your specific example you could use the decorator pattern in conjunction with interfaces to supplement your Word class with additional functionality; 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();
  }
}

In addition it's worth noting that the lack of multiple inheritence in Java isn't really the issue here; it's more a case of reworking your design. In general it's considered bad practice to over-use inheritence as deep inheritence hierarchies are difficult to interpret / maintain.

Adamski
WordExt has two copies of attribute kind. First "objWordExt.kind" inherited from AbstractWord. Second "objWordExt.word.kind" that it gets from instance variable word.
Gladwin Burboz
I considered using something like that but it will not really work in this case because some methods in length are quite long and complicated. Using the decorator I would need to implement them both in the Word class and in the WordDescriptor class. Thanks for the suggestion though.
sixtyfootersdude
@GB: Good point. Typically I'd define an interface Word which WordExt would implement to get round having a redundant copy of "kind".
Adamski
A: 

(I can't just add the field to the other one because of backwards compatibility, it break equals methods and stuff like that).

It won't break source compatibility. Not unless you're doing something really crazy in your equals methods.

And renaming your classes is generally not the way to handle binary compatibility.

Anon.
The equals for the WordDescriptor is based on all of the fields => broken backwards comparability.
sixtyfootersdude
I think you'll find that if two WordDescriptors are otherwise equal, they will have the same word length regardless. So no, it doesn't break source compatibility.
Anon.
+3  A: 

Just use composition instead of inheritance:

a BetterWord is-an AbstractWord that has-a Length:

public class BetterWord extends AbstractWord {

    private Length length;

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

EDIT

If the API needs an object of type Length, just add a getter:

public class BetterWord extends AbstractWord {

    private Length length;

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

    public Length getLength() {
        return length
    }
}

Or rename the implementation Length to LengthImpl and define an interface Length, because a class can implement multiple interfaces.

Andreas_D
@Andreas, totally agree about using "has-a" versus "is-a" but I would've thought setting the length of the word was something that wouldn't be modified for the object lifetime of a particular word.
Rob Wells
If there is an API that needs object of type Length, this will not work. See my solution below.
Gladwin Burboz
+2  A: 

Looking at it, my first feeling is that your model is a bit complicated.

A word has a String to describe the word itself being stored in the Word object along with a class to say it's a noun, verb, adjective, etc. Another property of a Word is the length of the string stored in the Word object.

Think of things in terms of "is-a" and "has-a" relationships and you can remove a lot of complexity.

For instance why do you need a WordDescriptor that extends AbstractWord? Is a word going to change from a verb to an adjective? I would have thought that the word type was set when the object was created and would not change during the lifetime of the Word object. Once you had a Word object for the word "Australia" the Kind of word would not change for the lifetime of the object.

Hmmm. Maybe you might have a Word object representing the word "bark" after instantiating the object with a type of "verb" to describe the sound a dog makes. Then you realise that you actually needed to have the Word object to represent a noun that describes the covering of a tree. Possible, but both the dog's bark and the tree's bark can exist.

So I think the model you've chosen is a bit too complicated and that your question can be resolved by going back and simplifying your original object model.

Start by asking yourself a question for each of the inheritance aspects of your basic model.

When I say Class B extends Class A, can I say that Class B "is-a" Class A and that I am specialising its behaviour?

For example, a base class Animal can be extended to provide the specialised class of Kangaroo. Then you can say that "the kangaroo "is-a" Animal. You are specialising the behaviour.

Then look at the attributes, a Kangaroo has a Location attribute to describe where it is found. Then you can say a Kangaroo "has-a" location. A Kangaroo "is-a" location doesn't make sense.

Similarly, a Word "has-a" length. And the statement a Word "is-a" length just doesn't make sense.

BTW All Australian references in this post are deliberate to celebrate Australia Day which is today 26th January!

HTH

Rob Wells
Wish you joyous Australia Day. I agree with your comments on WordDescriptor. It will be better if AbstractWord "has-a" field (with getter and setter) WordDescriptor.
Gladwin Burboz
+7  A: 

Favor composition over inheritance.

Solution takes into consideration that there could be another type of word that may need WordLengthSupport.

Similarly other interfaces could be created and implemented and various word types can have mix and match of those interfaces.

.

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

.

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

This solution does not use strategy pattern but can be refactored for same.

Gladwin Burboz
+1 Wow, that is an awesome solution, not super intuitive, but I think that should work quite cleanly.
sixtyfootersdude
Your function, private WordLength calculateWordLength(), does not return a value and should be void.
Joseph Gordon
That is right crosvenir. I wrote this in notepad so no syntax checking. Thanks for pointing out, I will update it.
Gladwin Burboz
Thanks Jake. I am glad that I was able to help.
Gladwin Burboz
A: 

The problem is not "how to deal with single inheritance". What you're missing is not really a design pattern but learning to design the API separately from the implementation.

I would implement it like so:

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

With this basic setup, when you want to introduce a length property, all you have to do is this:

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

The other answers that uses composition of properties as well as the Decorator pattern are also entirely valid solutions to your problem. You just need to identify what your objects are and how "composable" they are, and how they are to be used - hence designing the API first.

aberrant80
Thanks for the comment however maybe I was unclear what a WordDescriptor was. A word descriptor does not have a word. One WordDescriptor can describe many words. For example a WordDescriptor = noun can refer to all nouns. Therefore it would not make sense if a WordDescriptor contains a Word.
sixtyfootersdude