Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design using composition and interfaces in Java

I designed the following for a problem:

class Animal {
 // ...
}
class Guppy extends Animal { ... }
class Pigeon extends Animal { ... }

class TailedAnimal extends Animal {
 // ...
}
class Dog extends TailedAnimal { ... }
class Cat extends TailedAnimal { ... } 

class HornedAnimal extends Animal {
 // ...
}
class Ram extends HornedAnimal { ... }

public static void main(String[] args) {
 Animal a = getSomeAnimal();
 a.doSomething();
 if (a instanceof TailedAnimal) {
  // do something
 }
 if (a instanceof HornedAnimal) {
  // do something else
 }
}

Animal, HornedAnimal and TailedAnimal are used mainly as data models.

Since Java does not support multiple inheritance, I have trouble creating Rhinoceros which is a Horned and Tailed animal. After asking around, someone recommended using composition and interfaces. I came up with the following:

class Animal {
 // ...
}
class Guppy extends Animal { ... }
class Pigeon extends Animal { ... }
class Ram extends Animal implements IHorned { ... }
class Cat extends Animal implements ITailed { ... } 
class Dog extends Animal implements ITailed {
 BasicTail t = new BasicTail();
 public Object getTail() {
  return t.getTail();
 }
 public void setTail(Object in) {
  t.setTail(in);
 }
}

interface ITailed {
 public Object getTail();
 public void setTail(Object in);
 //...
}

class BasicTail implements ITailed {
 Object myTail;
 public Object getTail() { return myTail; }
 public void setTail(Object t) { myTail = t; }
}

interface IHorned {
 // getters and setters
}

public static void main(String[] args) {
 Animal a = getSomeAnimal();
 a.doSomething();
    // how do I check if a is horned or tailed?
}

My interface has getters and setters. Is there any way to avoid this? Assuming that there is currently no way to abstract the behaviour of Tails and Horns, and they're are being used mainly as data holders. How do I determine if my Animal is Horned or Tailed?

like image 235
Justin Wong Avatar asked Nov 29 '10 13:11

Justin Wong


People also ask

How do you design and use interface in Java?

An interface is declared by using the interface keyword. It provides total abstraction; means all the methods in an interface are declared with the empty body, and all the fields are public, static and final by default. A class that implements an interface must implement all the methods declared in the interface.

What is composition in Java with examples?

A composition in Java between two objects associated with each other exists when there is a strong relationship between one class and another. Other classes cannot exist without the owner or parent class. For example, A 'Human' class is a composition of Heart and lungs. When the human object dies, nobody parts exist.

What is the use of composition in Java?

Composition allows the reuse of code. Java doesn't support multiple inheritances but by using composition we can achieve it. Composition offers better test-ability of a class. By using composition, we are flexible enough to replace the implementation of a composed class with a better and improved version.

What is composition in OOP with example?

For example, your Horse class can be composed by another object of type Tail . Composition allows you to express that relationship by saying a Horse has a Tail . Composition enables you to reuse code by adding objects to other objects, as opposed to inheriting the interface and implementation of other classes.


2 Answers

I'd suggest strategy pattern here. In short:

interface TailedAnimal {
    void moveTail();
}
interface HornedAnimal {
    void hitWithHorn();
}
class Rhinoceros() implements TailedAnimal, HornedAnimal {
    private TailedAnimal tail;  //Instantiate it somehow e.g. constructor, setter
    private HornedAnimal horn;  //Instantiate it somehow e.g. constructor, setter
    public void moveTail() {
        tail.moveTail();
    }
    public void hitWithHorn() {
        horn.hitWithHorn();
    }
}

By using this you encapsulate behavior in a concrete implementation of the interfaces, and may easily share exactly the same behavior for a few animals, as well as change it at run-time.

like image 126
Przemek Kryger Avatar answered Oct 09 '22 01:10

Przemek Kryger


I think you must avoid setters in general. If you can, use immutable objects, and initialize its private data into its constructor.

To distinguish animals, I used another pattern, the visitor one. It's verbose, but you don't have to test directly what animal you're processing.

public class Animals {
private Animals() {
}

interface Animal {
    void accept(final AnimalProcessor visitor);
}

interface AnimalProcessor {
    void visitTailed(final TailedAnimal tailedAnimal);

    void visitHorned(final HornedAnimal hornedAnimal);
}

interface TailedAnimal extends Animal {
    void moveTail();
}

interface HornedAnimal extends Animal {
    void hitWithHorns();
}

static class Dog implements TailedAnimal {
    public void moveTail() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void accept(final AnimalProcessor visitor) {
        visitor.visitTailed(this);
    }
}

static class Cat implements TailedAnimal {
    public void moveTail() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void accept(final AnimalProcessor visitor) {
        visitor.visitTailed(this);
    }
}

static class Ram implements HornedAnimal {
    public void hitWithHorns() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void accept(final AnimalProcessor visitor) {
        visitor.visitHorned(this);
    }
}

static class Rhinoceros implements HornedAnimal, TailedAnimal {
    public void hitWithHorns() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void moveTail() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public void accept(final AnimalProcessor visitor) {
        visitor.visitTailed(this);
        visitor.visitHorned(this);
    }
}

public static void main(String[] args) {
    Collection<Animal> animals = new ArrayList<Animal>(Arrays.asList(new Dog(), new Cat(), new Rhinoceros()));
    for (final Animal animal : animals) {
        animal.accept(new AnimalProcessor() {
            public void visitTailed(final TailedAnimal tailedAnimal) {
                // you do what you want when it's a tailed animal
            }

            public void visitHorned(final HornedAnimal hornedAnimal) {
                // you do what you want when it's a horned animal
            }
        });
    }
}
}
like image 24
sly7_7 Avatar answered Oct 09 '22 01:10

sly7_7