Reputation: 331
I am creating an interface say 'Car'
public interface Car {
public void drive(int Speed); // for cars which do not have gears
}
which has multiple implementation like Bus, Truck and etc Then in a main class called Trafic, the drive method of all the implementaions should be called (the order does not matter). I have two option for Traffic Class design:
1) either use a property for every Car implementation which makes the consrtuctor hard to maintain and ugly (with many arguments of same type and the fact that by adding new Car implementation it should be changed) like this:
public class Traffic {
private Car bus;
private Car truck;
...
public Traffic(Car bus, Car truck,...){
this.bus = bus;
this.truck = truck;
...
}
public void run(){
bus.drive();
truck.drive();
...
}
}
2) or pass a list of Car's to Traffic. Then what if i want to check something else about the Bus, so sometimes i need to find an implementation with Instance Of. It would be something like this:
public class Traffic {
private List<Car> cars;
public Traffic(Car bus, Car truck,...){
this.bus = bus;
this.truck = truck;
...
}
public void run(){
for(Car car : cars){
car.drive();
}
}
}
I feel kind of unsatisfied with either of these solutions. Is there any other solution?
Upvotes: 0
Views: 2698
Reputation: 29385
what if i want to check something else about the Bus
In polymorphic design you should create a functionality delegating method that is called on the higher level and put the implementation specific logic forking there. Sometimes this means creating a whole mini-DI helper class to provide extra services to the polymorphic implementations of parent class which can make things look a bit weird but at the same time there really isn't a way around it when in OOP.
Consider the following class hierarchy:
public abstract class Vehicle {
public abstract void drive();
}
public abstract class Car extends Vehicle {
public void drive() {
System.out.println("Driving around...");
}
public void honkHorn() {...}
}
public final class Bus extends Car {
}
public final class Truck extends Car {
@Override
public void drive() {
super.drive();
honkHorn();
}
}
Here I have augmented your class hierachy with two additional features:
Vehicle
because maybe you want to expand to boats which are also driven but definitely not in the same way as cars.Truck
honks its horn every time it is also drivenThe latter part is the important one here; one of the bigger benefits of polymorphism is introducing extra functionality like this in subclasses. If external dependencies exist and state checking is required - for example we really don't want to drive our boats on highways - you can introduce a helper on top level to provide the implementations with extra details. So, changing Vehicle
like this...
public abstract class Vehicle {
public abstract void drive(Surface surface);
}
now allows us to define a Boat
...
public abstract class Boat extends Vehicle {
@Override
public void drive(Surface surface) {
if (surface.isWater()) {
doDrive(surface);
}
}
// this is needed to ensure that extending classes really do implement the driving capability!
public abstract void doDrive(Surface surface);
}
which limits the applicability of all Boat
implementations to only watery surfaces.
To bring all this together with your Traffic
class you can now start to consider what is the proper object hierarchy for dealing with various aspects of the class modeling given here. You could for instance define that Traffic
happens on Surface
and the class handles only moving instances of Vehicle
s on Lane
s and the logic would query for eg. vehicle dimensions and speed to consider such cases as when a bus can switch lanes after picking up passengers from a stop or if the lane is already filled to brim and is moving too slow to warrant a lane change at all.
To answer your question, internally you'll probably want to use a List<Vehicle>
in any case as that provides ordering for your vehicles, but the Traffic
class should not take in a list of vehicles as in case where Traffic
represents a highway you really don't join the traffic from random spots but from specific intersections/junctions which insert the incoming vehicle to specific spot in relation to the highway. So, you probably want to create a few methods for inserting/removing Vehicle
s into and out from Traffic
at specific points. In this scenario the internal list probably should contain wrapper objects to augment the vehicle info with the relative position of the vehicle on the lane to make inserting new vehicles easier and in general to update all the positions in one easy loop, but this post is now getting really long and I'm assuming a lot here... :)
Upvotes: 2
Reputation: 131546
You could use a Varargs as argument and you should use generics in your collection it is safer.
public class Traffic {
private List<Car> cars;
...
public Traffic(Car... cars){
this.cars = Arrays.asList(cars);
}
...
}
If you want to have a List
which has not the limitations of the list returned by Arrays.asList()
you could do that :
public class Traffic {
private List<Car> cars;
...
public Traffic(Car... cars){
this.cars = new ArrayList<>();
this.cars.addAll(Arrays.asList(cars));
}
...
}
Upvotes: 2