Question

I am working on low level design of cab booking type of system and feeling stuck at modelling Vehicle in Booking class.I came up with following design.

    Class Vehicle
    {
        
        
    }
    Class Car extends Vehicle
    {
        CAR_TYPE type;
    }
    Classs Bike extends Vehicle
    {
        
    }
    Class Auto extends Vehicle
    {
        
    }
    Class CAR_TYPE
    {
    HATCHBACK,
    SEDAN,
    SUV,
    XUV
    }
    Class Booking
    {
        int number;
        ...
        ....
        Vehicle Vehicle;
        VehicleType type;
        ...
        ...
    }
    enum VehicleType
    {
        CAR,
        BIKE,
        AUTO
        
    }

Now to see, which vehicle is used for booking, we can see the VehicleType from booking and then typecast the vehicle to that object(CAR,BIKE,AUTO).Is it a bad way model?

In general, if we have a reference to parent, and we want to know specific child class which is holds,is this a good way to do?Are there alternate approaches ?

Was it helpful?

Solution

Why it's not the best way?

Getting the type from the parent (Booking) and type-casting/down-casting the child (Vehicle) to its true type (e.g. Car) works but it's not the best way, because:

  • It does not allow to freely extend Vehicle: if tomorrow, you'd want to book also Boats or Trucks you'd need to check and update all the code using Booking. If not, you'd risk some failures (e.g. a Booking instance uses a Boat, but the client code doesn't expect it raises and exception (or does wrong downcast). This is not in line with the open/closed principle.
  • Moreover it requires the classes to know a lot about its neighbours (Booking) and the neighbours of your neighbours (Vehicle) and its possible specializations. This is not in line with the principle of least knowledge.

What are the alternatives?

Your question does not provide enough information to propose the best alternatives. But here some approaches:

  • Tell, don't ask!: instead of asking and asking and do yourself a part of the processing logic, tell the Booking what you want, let the Booking tell the Vehicle what it shall do accordingly, and let polymorphism drive the objects to do what is needed depending on their type.

  • If polymorphism is not sufficient, because some Car behaviors do not exist for Bike or Boat, you may consider:

    • using a strategy for Booking that is adapted to the type of Vehicle, and inject this strategy togeter with the Vehicle.
    • using a visitor pattern to process the composite structure made of Booking and its associated objects
    • having specialization of Booking that correspond to the appropriate specialization of Vehicle (e.g. CarBooking for Car, BikeBooking for Bike). Typically, you'd use an abstract factory pattern to create families of related objects (e.g. specialized Booking and specialized Vehicle).

OTHER TIPS

Here’s what you do: You accept that right now you don’t know yet what you need, and make sure that you can adapt to what you need when you figure it out.

For the booking, for example, I wouldn’t store car type and vehicle type but an abstract type describing what the customer wants. Which might include “not red”. In the future. If designed right, your code will survive that change with no code change.

In my opinion from a booking perspective you don't realy need to model via inheritence each individual transport type. Are there any actions or functions attached to these transport types so that you need to model them explicitly? Same goes for the enum, do you want for any new transport type in the future to actually do a code change ?

In my opinion a more generic model will fit better in this case than trying to model each individual transport type.

Use one Class Vehicle with attribute Vehicle type1 that is a String like this:

public class Vehicle {

   private String vehicleClass;
   private String vehicleType;

}

If you need more data than this you can aways add a Map with vehicleAttributes or something around these lines.

By reserving one attribute as discriminator for example vehicleClass, in the future if you need a more concrete modeling you can introduce it.

All you need to do is accurately model your problem domain. Your problem domain appears to be some sort of taxi or ride sharing service, not manufacturing vehicles.

You do not need to perfectly model all the world's vehicles. Just enough for customers to choose the appropriate ride and book their trip.

By having sub types of Vehicle, you are saying that you expect behavioral differences between Car, Bike and Auto (you could argue that "car" and "automobile" are the same thing). You haven't stated what those differences are, but you should analyze whether they are differences in behavior or data. Differences in data should be captured in fields or properties of an object, not different types.

The customer only cares about whether the vehicle is a sedan, hatchback, truck, bike, etc. I would just have a VehicleType on a Vehicle object. No need for specialization beyond that. If you do need some sort of specialization consider putting that in the VehicleType enum. Certain operations on the Vehicle object could delegate to methods on the VehicleType enum for behavior specific to that kind of vehicle.

Licensed under: CC-BY-SA with attribution
scroll top