-1

Let's say I want a class MyClass that, among other things, has a property representing a vehicle.

The vehicle can be a car or motorcycle.

In case of a car, I want to be able to retrieve its steering wheel.

EDIT: My fundamental assertion is that a motorcycle does not have a steering wheel, so I would like to avoid having something like a getSteeringWheel in a Motorcycle class.

I see two solutions :

  1. An abstract Vehicle class extended by two classes : Car and Motorcycle

The problem is that from a MyClass object, to retrieve the type of steering wheel I have to do something like this:

Vehicle vehicle = getVehicle();
if (vehicle instanceof Car) {
    SteeringWheel steeringWheel = ((Car) vehicle).getSteeringWheel();
}

which I suppose is not very good.

  1. A concrete Vehicle class containing everything

I would then do something like this:

Vehicle vehicle = getVehicle();
if (VehicleTypeEnum.CAR.equals(vehicle.getType())) {
    SteeringWheel steeringWheel = vehicle.getSteeringWheel();
}

But Vehicle would be an akward class, because if the object's type is MOTORCYCLE then the getSteeringWheel() method does not have much sense.

Which of is better? Is there another solution? Thank you!

Wenneguen
  • 3,196
  • 3
  • 13
  • 23
  • you could also use an interface `HasSteeringWheel` and implement it only on the classes that have a steering-wheel – TmTron Jun 01 '18 at 16:58
  • I would still have to implement the hasSteeringWheel method into the Motorcycle class, which I would like to avoid because I consider that a motorcycle does not have a steering wheel. – Wenneguen Jun 01 '18 at 18:48
  • 2
    Can you back up and explain why code that can usefully interact with `Vehicle` sometimes cares about the `SteeringWheel` of a `Car`? `Vehicle` is an abstraction, and they should be focused on operations, not properties. That is, instead of getting the steering wheel of a car and manipulating it, you should tell the vehicle (or its driver) where to go, and let it interpret the message appropriately. A driver will use his car's steering wheel, a rider will use her motorcycles handlebars, a computer will set switches appropriately to guide a train to its destination, etc. – erickson Jun 01 '18 at 19:19
  • Could you just implement it on motorcycle and return null. then you just perform a null check? maybe thats bad idk – Zannith Jun 01 '18 at 20:08
  • @erickson I understand what you say, I'll try and see how I can apply it in my case. Thanks! – Wenneguen Jun 01 '18 at 21:35
  • @Wenneguen no - Motorcycle would not need to implement the interface - only all classes that actually have a steering wheel (they don't even need a common base class). You will however still need an instance-of check: but not on the concrete class/es (Car, Truck, Ship, etc.), but only on the abstract `HasSteeringWheel` interface. – TmTron Jun 02 '18 at 07:18

2 Answers2

3

Avoid using instanceof, as it will break your OOP design. Instead, find the common thing that a Car and a Motorcycle have. Both have a part that does the steering. Extract that into a superclass, and implement in subclasses the actual steer.

public abstract class Vehicle {
   public abstract Steer getSteer();
}



public abstract class Steer {
   public abstract left();
   public abstract right();
}


public class Car extends Vehicle {
    @Override
    public SteeringWheel getSteer() {
        return wheel;
    } 
}

public class Motorcycle extends Vehicle {
    @Override
    public SteeringBar getSteer() {
        return bar;
    } 
}

(Being non-native, I'm not sure if 'steer' is actually an English noun)

M. le Rutte
  • 3,525
  • 3
  • 18
  • 31
  • Maybe my example was a bad one, but my fundamental assertion is that a motorcycle does not have a steering wheel, so I would like to avoid having something like a getSteeringWheel in a Motorcycle class. – Wenneguen Jun 01 '18 at 18:41
  • @Wenneguen This answer is along the lines of what I was suggesting as well. Whatever interacts with `Vehicle` can do so without having to downcast to a subtype. – erickson Jun 01 '18 at 22:07
  • @erickson My bad, I misread your answer a little. But still, you managed to find an equivalent of the steering wheel (the steering bar) which demonstrates how bad my example is. But how would you proceed with something that a car has and for which a motorcycle does not have any equivalent? – Wenneguen Jun 01 '18 at 22:28
  • @Wenneguen What is trying to access the steering wheel and what will they do with it? I like this answer because it focuses on the operations (methods) rather than data. You can get a "Steer" and send it left or right messages; it doesn't matter if it's a steering wheel, handlebar, or the control lines of a paraglider. Even if it's a train with no steering mechanism, you could return a `NoOpSteer` that does nothing. – erickson Jun 01 '18 at 22:50
1

Sorry if the question was poorly worded, but I think that with your help I managed to solve my problem. Currently I have something like this:

public class MyClass {

private Vehicle vehicle;

public void doStuff() {

    doNonVehicleStuff();

    Vehicle vehicle = getVehicle();

    doRegularStuff(vehicle);

    if (vehicle instanceof Car) {
        SteeringWheel steeringWheel = ((Car) vehicle).getSteeringWheel();
        doSteeringWheelStuff(steeringWheel);
    }
}

But I should actually do something like this:

public void doStuffWithVehicle() {    

    doNonVehicleStuff();

    getVehicle().doStuff();
}

With Car overriding the doStuff method to include doSteeringWheelStuff in addition to the regularStuff.

Wenneguen
  • 3,196
  • 3
  • 13
  • 23