Frage

I'm still a beginner in java, but I try to write good code (obj oriented). However, I'm having a problem with the method removeFromWorld. I several approaches, but whatever I do I seem to break a "rule" of good programming practice.

I tried it with type-checking:

    public class World{
        private Set setGiraffes;
        public void removeFromWorld(Animal animal){
            if (isGiraffe(animal))
                setGiraffes.remove((Giraffe) animal)
            else if (isZebra(animal)){...}
        else if ...
        }
    }
    public abstract class Animal{..}

    public class Giraffe extends Animal{..}

but heard that was a bad idea since it's impossible to add a new animal without changing existing methods. I considered moving removeFromWorld to Animal and overwriting it in each subclass, but since it's World using the sets, that seemed bad as well

I'm quite at a loss as for what would be an "elegant"/good solution.

War es hilfreich?

Lösung

You are absolutely right, this style of programming kills maintainability of your code right away.

There are two simple ways of handling this - implementing a visitor, and defining a Map based on Class<T>.

Here is an example of the first approach:

interface Visitor {
    void visitGiraffe(Giraffe g);
    void visitZebra(Zebra z);
}
abstract class Animal {
    public abstract void accept(Visitor v);
}
class Giraffe extends Animal {
    public void accept(Visitor v) {
        v.visitGiraffe(this);
    }
}
class Zebra extends Animal {
    public void accept(Visitor v) {
        v.visitZebra(this);
    }
}

With this structure in hand, you can write your remover as follows:

void removeFromWorld(Animal a) {
    a.accept(new Visitor() {
        public void visitGiraffe(Giraffe g) {
            setOfGiraffes.remove(g);
        }
        public void visitZebra(Zebra z) {
            setOfZebras.remove(z);
        }
    });
}

The second relies on the ability of Java objects to produce their Class. Now instead of defining

Set<Giraffe> setOfGiraffes = ...
Set<Zebra> setOfZebras = ...

you can define

Map<Class,Set<Animal>> setOfAnimalByClass = ...

To access giraffes, you would do

setOfAnimalByClass.get(Giraffe.class).add(new Giraffe());

and so on. Then you can implement removeFromWorld like this:

void removeFromWorld(Animal a) {
    a.accept(new Visitor() {
        setOfAnimals.get(a.getClass()).remove(a);
    });
}

Andere Tipps

Assuming all animals hava a removeFromWorld method but each child has a different version then the elegant solution is to make Animal an abstract class and removeFromWorld an abstract method. That way any class that extends Animal has to have its own removeFromWorld method (and cannot accidently use the generic Animal one). Whether you actually want to do that or not though will depend on your actual application

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top