views:

226

answers:

4

Lets say we have this interface:

interface IVehicle { ... }

And some classes implementing it:

class Car : IVehicle { ... }
class Boat : IVehicle { ... }
class Plane : IVehicle { ... }

In my user interface I have a FlowLayoutPanel and access to some sort of an IEnumerable<IVehicle> with a number of various IVehicle objects.

Now I want to create a UserControl for each of the vehicles and add it to the FlowLayoutPanel. The controls will be kind of similar, but since there are vehicles of different types some of the controls might need to look slightly different or work in a different way so that the user can easily work with his vehicles. How can I best solve this without too much clutter?

+2  A: 

How about using some sort of factory method:

UserControl CreateControl(IVehicle vehicle) 
{
    if (vehicle is Car)
    {
        return new CarControl();
    }
    else if (vehicle is Boat)
    {
        return new BoatControl();
    }
    ...
}
Patrick McDonald
I think this would be the simplest option, although I was hoping to avoid a long `if else if` or `switch` statement. Will probably end up with this though :p
Svish
+1  A: 

I am not sure what exactly you are aiming for, but you can use generics when you extend user controls in the usual way:

public class VehicleControl<TVehicle>: UserControl
where TVehicle:IVehicle
{
  //do something specific with IVehicle 
}

public class CarControl : VehicleControl<Car>
{
  //add stuff specific for the Car
}
Grzenio
How would you instantiate the controls?
Svish
I could use a factory method proposed by @Patrick McDonald in his answer. Our answers are complimentary.
Grzenio
Ah, gothca. interesting idea.
Svish
A: 

If you know when writing the code how the mapping looks like, you can set up at Dictionary with mappings:

private Dictionary<Type, Type> _vehicleToControlTypeMappings = new Dictionary<Type, Type>();

Load mappings at startup:

_vehicleToControlTypeMappings.Add(typeof(Car), typeof(CarControl));
_vehicleToControlTypeMappings.Add(typeof(Plane), typeof(PlaneControl));

...and provide a method for getting a new control based on a vehicle:

private Control GetVehicleControl(IVehicle vehicle)
{
    Control result = (Control)Activator.CreateInstance(
        _vehicleToControlTypeMappings[(vehicle as object).GetType()]
        );
    // perform additional initialization of the control
    return result;
}

Then you can simply pass objects of types that implements IVehicle into the method:

IVehicle vehicle = new Car();
Control newctl = GetVehicleControl(vehicle);
Fredrik Mörk
Looks fancy and I was thinking about something similar. But isn't this mostly a complex way of doing what Patrick McDonald did in his answer with an `if else if`?
Svish
The main difference is that I feel that this approach has a cleaner separation of concerns; the factory method does not have any knowledge about the mappings as such, it just performs a lookup. That means that you can change how the mapping dictionary is populated (for instance it could be loaded based on information in a config file) without altering the factory method.
Fredrik Mörk
I think this kind of complication makes sense only if you are planning to add new vehicle classes fairly often.
Grzenio
@Fredrik Good point. Have to agree with Grzenio though.
Svish
+2  A: 

In your interface IVehicle, you could add a method to get the user control.

public interface IVehicle
{
    UserControl GetVehicleControl();
}

If you need a control for each vehicle, you can use this code:

public class Car : IVehicle
{
    public UserControl GetVehicleControl()
    {
         return new CarControl();
    }
}

Else if you need only one control for each vehicle type:

public class Car : IVehicle
{
    private static CarControl m_control;

    public UserControl GetVehicleControl()
    {
         if(m_control == null)
             m_control = new CarControl();

         return m_control;
    }
}
Francis B.
why do you make CarControl static? So multiple vehicules share the control?
Scoregraphic
@Scoregraphic: Yes in the case that only one CarControl is needed for all cars. But I think the OP want a control for each car, boat, etc... so the version with no static variable is the best one.
Francis B.
But if I do this the vehicles, which are pretty much just business objects, would have to know about their visual representation. This isn't very good design is it?
Svish
@Svish: I agree with you, it binds your business objects with the design. I'm not aware of the complete architecture of your system and maybe this solution is not suitable or applicable for you.
Francis B.