I have java interface and class implementations that have need for different arguments when invoking similar behavior. Which of the following is mostly appropriate?
In first option I have different classes inherit common behavior from base interface and all differences are only implemented directly in the classes and not in interface. This one seems most appropriate, but I have to do manual type-cast in the code.
public class VaryParam1 {
static Map<VehicleType, Vehicle> list = new HashMap<>();
public static void main(String[] args) {
list.put(VehicleType.WITHOUT_TRAILER, new Car());
list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());
((Car)list.get(VehicleType.WITHOUT_TRAILER)).drive(1); //ok - but needed manual cast
((TruckWithTrailer)list.get(VehicleType.WITH_TRAILER)).drive(1, 1); //ok - but needed manual cast
}
}
enum VehicleType {
WITHOUT_TRAILER,
WITH_TRAILER;
}
interface Vehicle{
//definition of all common methods
}
class Car implements Vehicle {
public void drive(int numberOfDoors) {
System.out.println(numberOfDoors);
}
}
class TruckWithTrailer implements Vehicle {
public void drive(int numberOfDoors, int numberOfTrailers) {
System.out.println(numberOfDoors + numberOfTrailers);
}
}
In second option I have moved methods one level up to the interface, but now I need to implement behavior with UnsupportedOpException. This looks like code smell. In code, I don't have to do manual casting, but I also have possibility to call methods that will produce exception in run time - no compile time checking. This is not that big problem - only this methods with exception that look like code smell. Is this way of implementation best practice?
public class VaryParam2 {
static Map<VehicleType, Vehicle> list = new HashMap<>();
public static void main(String[] args) {
list.put(VehicleType.WITHOUT_TRAILER, new Car());
list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());
list.get(VehicleType.WITHOUT_TRAILER).drive(1); //works
list.get(VehicleType.WITH_TRAILER).drive(1, 1); //works
list.get(VehicleType.WITHOUT_TRAILER).drive(1, 1); //ok - exception - passing trailer when no trailer - no compile time check!
list.get(VehicleType.WITH_TRAILER).drive(1); //ok - exception - calling trailer without trailer args - no compile time check!
}
}
enum VehicleType {
WITHOUT_TRAILER,
WITH_TRAILER;
}
interface Vehicle{
void drive(int numberOfDoors);
void drive(int numberOfDoors, int numberOfTrailers); //code smell - not valid for all vehicles??
}
class Car implements Vehicle {
@Override
public void drive(int numberOfDoors) {
System.out.println(numberOfDoors);
}
@Override
public void drive(int numberOfDoors, int numberOfTrailers) { //code smell ??
throw new UnsupportedOperationException("Car has no trailer");
}
}
class TruckWithTrailer implements Vehicle {
@Override
public void drive(int numberOfDoors) { //code smell ??
throw new UnsupportedOperationException("What to do with the trailer?");
}
@Override
public void drive(int numberOfDoors, int numberOfTrailers) {
System.out.println(numberOfDoors + numberOfTrailers);
}
}
Here I used generics in order to have common method in interface, and parameter type is decided in each class implementation. Problem here is that I have unchecked calls to drive. This is more-less similar to problem of direct casting in option 1. Bur here I also have possibility to call methods that I should not be able to!
public class VaryParam3 {
static Map<VehicleType, Vehicle> list = new HashMap<>();
public static void main(String[] args) {
list.put(VehicleType.WITHOUT_TRAILER, new Car());
list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());
list.get(VehicleType.WITHOUT_TRAILER).drive(new VehicleParam()); //works but unchecked call
list.get(VehicleType.WITH_TRAILER).drive(new TruckWithTrailerParam()); //works but unchecked call
list.get(VehicleType.WITHOUT_TRAILER).drive(new TruckWithTrailerParam()); //works but should not!
list.get(VehicleType.WITH_TRAILER).drive(new VehicleParam()); //ClassCastException in runtime - ok but no compile time check
}
}
enum VehicleType {
WITHOUT_TRAILER,
WITH_TRAILER;
}
class VehicleParam {
int numberOfDoors;
}
class TruckWithTrailerParam extends VehicleParam {
int numberOfTrailers;
}
interface Vehicle<T extends VehicleParam>{
void drive(T param);
}
class Car implements Vehicle<VehicleParam> {
@Override
public void drive(VehicleParam param) {
System.out.println(param.numberOfDoors);
}
}
class TruckWithTrailer implements Vehicle<TruckWithTrailerParam> {
@Override
public void drive(TruckWithTrailerParam param) {
System.out.println(param.numberOfDoors + param.numberOfTrailers);
}
}
So question is - which of this 3 options is the best one (or if there is some other option I have not found)? In terms of further maintenance, changing etc.
Aucun commentaire:
Enregistrer un commentaire