I have several classes Parent and Child1 ... Child9 implemented in Java. Parent is an abstract class, containing all the common variables of the child classes (a lot, which is the main reason why I made Parent an abstract class and not an interface), some abstract and some implemented methods.

Some of the child classes have custom methods which are specific to them. So often I find myself calling a child method using downcasting:

Parent p = new Child1();
((Child1) p).child1SpecificMethod();

Somehow I have the feeling that this is bad OOD practice, but I am not quite sure if that really is the case respectively how to improve the design.

- Edit - What I probably should change anyway is the fact that I use the Parent class for organizing many (for now) common variables, making them (or a container object) members of the concrete classes.

有帮助吗?

解决方案

This is not only bad practice, this is unnecessary complicated.

Why do you use inheritance in general?

When you use inheritance, you have a common set of behaviour, which you want to make available for many different carriers. This includes class inheritance as well als interface inheritance. The heir, so to speak, is oftentimes a specialization of the class from whom it inherits; which is mainly true for class inheritance.

Think of a class car and a subclass porsche (the typical is a-relationship). You have general behaviour like starting/stopping engine, steering and so on. If you treat a porsche like a car, you are bound to this aspect of its behaviour. If you know, that you only want a porsche and only deal with it as a porsche, it is redundant to instantiiate a porsche as a car and get porsche-behaviour via casting.

Polymorphism makes sense the other way around:

You have a porsche and need to treat it from the aspect of a car; e.g. driving

As long as your porsche accepts steer left, steer right, shift up, shift down, etc. you could make use of polymorphism/substitution one for the other.

It is better, to instantiate your objects in their specialized form. Then you could make the most of polymorphism and use it only when you need it.

That said: Parent p = new Child1(); makes no sense for me.

Edit: I would implement porsche different (via composition), but for the sake of the example, it is a car.

其他提示

Your feeling is right to consider it a bad practise. Imagine your example code a little different:

Parent p = createObject();
((Child1) p).child1SpecificMethod();

How do you know, that the value of p is really Child1? You don't and this can cause a ClassCastException at runtime. If you need to call child1SpecificMethod() in your code you have to make sure that p is of type Child1. If that is not possible because Object p is passed to your code (e.g. as a method parameter) as type Parent, you could consider using a variant of the Visitor-Pattern and execute child1SpecificMethod in the handle-Method of your visitor object, that handles Child1.

Use lookup of capabilities instead. Give no access to the child classes, consider them implementations of the Parent class.

Then define interfaces specifying some capability, feature.

interface Child1Specific {
    void child1SpecificMethod();
}

Usage:

Parent parent = ...
Child1Specific specific = parent.lookup(Child1Specific.class);
if (specific1 != null) {
    specific1.child1SpecificMethod();
}

This discovery mechanism is very flexible. Using delegation instead of inheritance can be quite rewarding. Note, having child classes is no longer needed.

Or in java 8 (where several variations are possible, and the interface could be functional too):

Optional<Child1Specific> specific = parent.lookup(Child1Specific.class);
if (specific1.isPresent()) {
    specific1.get().child1SpecificMethod();
}

Make in the Parent class a lookup for some capability:

public class Parent {
    protected final Map<Class<?>, Object> capabilities = new HashMap<>();
    protected final <T> void registerCapability(Class<T> klass, T object);

    public <T> T lookup(Class<T> klass) {
        Object object = capabilities.get(klass);
        return object == null ? null : klass.cast(object);
    }

Or in java 8:

    public <T> Optional<T> lookup(Class<T> klass) {
        Object object = capabilities.get(klass);
        return Optional.ofNullable(klass.cast(object));
    }

The child class:

class Child1 extend Parent implements Child1Specific {
    Child1() {
        registerCapability(Child1Specific.class, this);
    }
}

Or more dynamic:

class Child1 extends Parent {
    private Child1Specific specific = new Child1Specific() {
        ... Parent.this ...
    };
    Child1() {
        registerCapability(Child1Specific.class, specific);
    }
}

Add an abstract method child1SpecificMethod() in Parent class(you need to mark class as abstract) and provide its implementation in respective child class.

许可以下: CC-BY-SA归因
scroll top