Reputation: 33
I'm searching a clean pattern to solve the following problem:
I have several type of vehicles (let's say Car, Bike and Truck, all these classes expand the abstract class Vehicle). Each type of vehicle has several specific properties.
I have somewhere a list of vehicle, and i need to build a GUI which makes the user able to select a vehicle, and edit its properties. The result I'd like to achieve is to display a vehicle list (using JList) on the left, and a panel on the right containing the fields needed to edit the selected Vehicle.
I know how to do it when the list contains only one type of items (I use a Jlist on the left, and a custom JPanel on the right). I made one custom panel for each subclass : CarPanel, BikePanel... My problem is the link between the type of the selected Vehicle and the according panel. Although it would work, I'd like to avoid something like that :
if (selectedVehicle instanceof Car) {
useThisPanel(new CarPanel((Car)selectedVehicle));
} ...
because it doesnt seems very maintainable to me.
I also want to avoid something like useThisPanel(selectedVehicle.getPanel())
because there is no reason any vehicle class should know anything about the way it is displayed
I'm currently working on switching my code to a MVC pattern, but it seems to me it's not enought to solve my problem.
I'm pretty sure I'm not the first one to face this, but I couldn't find any answer or advice to manage this kind of situations.
Upvotes: 0
Views: 99
Reputation: 692131
The usual way of solving this problem in an OO way is to use the visitor pattern:
public interface VehicleVisitor<T> {
T visitCar(Car car);
T visitTruck(Truck truck);
T visitBike(Bike bike);
}
public abstract class Vehicle {
public abstract <T> T accept(VehicleVisitor<T> visitor);
}
public class Car extends Vehicle {
@Override
public <T> T accept(VehicleVisitor<T> visitor) {
return visitor.visitCar(this);
}
}
// same for Bike and Truck
And finally
public class SomeClass {
private JPanel createPanelFor(Vehicle vehicle) {
return vehicle.accept(new VehicleVisitor<JPanel>() {
@Override
public JPanel visitCar(Car car) {
return new CarPanel(car);
}
// same for Truck and Bike
}
}
}
This is clean and everything, but it's a bit more difficult to understand than a simple instanceof based solution. It has an advantage though: you can't forget handling a new Vehicle type: it will force you to implement the abstract accept()
method, which will force you to add another method to the Visitor interface, which will force you to implement it in every visitor.
Upvotes: 1