none
How do I accomodate different inherited objects in a collection?

    Question

  • Given a base class of VehicleBase that implements the properties of Engine and Transmission and then subclasses of Car and Truck that inherit from the VehicleBase Class and then provide additional properties. The Car class implements an Axel property to use at most two axels and four tires. The Truck class implements an Axel property to allow for 2 or 3 axels, one must only have two tires but the others can have two or four tires each. Additionally, the Truck class provides for a PTO (Power Take Off) property.

    Now I have a Garage collection class that I want to use to store various types of vehicles. How can I define this collection to accept any type of vehicle and when I select an object from the collection how do I know what type of vehicle it is?

    For Example:

    Car1 = new Car with {Axel=2, Tires=4,Engine="2.4 Liter", Transmission="Automatic"}

    Car2 = new Car with {Axel=2, Tires=4, Engine="5.0 Liter", Transmission="Manual"}

    Truck1 = new Truck with {Axel=3,Tires=10,Engine="6.5 Liter", Transmission="Automatic", PTO=True}

    Truck2 = new Truck with {Axel=2,Tires=4,Engine="4.5 Liter", Transmission="Manual", PTO=False}

    MyGarage = new Container<???>  ‘How should the object container be defined?

    MyGarage.Add(Car1)

    MyGarage.Add(Car2)

    MyGarage.Add(Truck1)

    MyGarage.Add(Truck2)

    For Each Vehicle in MyGarage   ‘How should the Vehicle variable be defined?

                    ‘Do something with each vehicle

                    ‘How do I know if I can access the PTO property?

    Next Vehicle

    Now let’s say I want to track the tire pressure for each vehicle before sending it out for the day. So the driver needs to capture this information. For a car and a truck with only four tires I need a structure that has only four pressures and the date but for a truck I may need a structure that has 10 pressures and a date:

    Structure TireInspection

    LeftFrontPressure

    RightFrontPressure

    LeftRearPressure

    RightRearPressure

    InspectionDate

    End Structure

    This structure implemented for a Truck with only 2 axles may look like:

    Structure TireInspection

    LeftFrontPressure

    RightFrontPressure

    LeftInnerRearPressure

    LeftOuterRearPressure

    RightInnerRearPressure

    RightOuterRearPressure

    InspectionDate

    End Structure

    I know these capabilities can be implemented differently but the point is how do I allow for different data structures and properties in child classes? Do I need to implement different containers? That would seem to defeat the purpose of inheritance. Using virtual properties in the base class would allow each subclass to handle the number of tires independently but what do I do with properties that are implemented in one child but not another? If I print an inspection report how do I know when to print 4 tires and when to print 10? Or, more to the point, How do I know when to use "Vehicle.TireInspection.LeftRearPressure" or "Vehicle.TireInspection.LeftInnerRearPressure"?

    Monday, November 30, 2015 12:02 AM

Answers

  • >Now I have a Garage collection class that I want to use to store various types of vehicles. How can I define this collection to accept any type of vehicle and when I select an object from the collection how do I know what type of vehicle it is?

    You'd have a collection of the base class type.

    The trick to retaining elegance with derived classes is that ideally
    the calling code should not need to know anything about the derived
    type behaviours - hide such differences behind the base class virtual
    methods so that each derived class does what's specific to their
    class.

    If there are aspects of some derived classes that are not applicable
    to others, have the others do dummy implementations that would either
    do nothing, or perhaps return a "not applicable to me" indication.

    Dave

    • Marked as answer by Ron Matuszek Tuesday, December 01, 2015 10:42 AM
    Monday, November 30, 2015 8:39 AM

All replies

  • >Now I have a Garage collection class that I want to use to store various types of vehicles. How can I define this collection to accept any type of vehicle and when I select an object from the collection how do I know what type of vehicle it is?

    You'd have a collection of the base class type.

    The trick to retaining elegance with derived classes is that ideally
    the calling code should not need to know anything about the derived
    type behaviours - hide such differences behind the base class virtual
    methods so that each derived class does what's specific to their
    class.

    If there are aspects of some derived classes that are not applicable
    to others, have the others do dummy implementations that would either
    do nothing, or perhaps return a "not applicable to me" indication.

    Dave

    • Marked as answer by Ron Matuszek Tuesday, December 01, 2015 10:42 AM
    Monday, November 30, 2015 8:39 AM
  • Ok, I think I get it. So for my example I would never try to access any internal tire pressure structure. I would let the individual child object encapsulate those implementation details, keep them hidden. I would just have each child object implement a report method that would instead return a common report object that would present the date tire pressures were measured and however many pressures that would be appropriate for the child involved. For example a dictionary with a date entry and four "TirePosition":"TirePressure" pairs or ten pairs if necessary.

    As for the PTO value, I would implement a default property within the base object that would return a "I don't have one of those." response and then have those vehicles that do have a PTO override that default response with whatever functionality that is appropriate. That way any existing code that doesn't know about that capability never needs to be changed and any new child that wants to implement that property just overrides the default from the parent. Any calling code that wants to know about the vehicle's PTO would need to handle the "I don't have one of those." response in addition to whatever may be returned by a child that does have a PTO.

    Thanks Dave.

    Ron

    Tuesday, December 01, 2015 10:42 AM
  • Ok, I think I get it. So for my example I would never try to access any internal tire pressure structure. I would let the individual child object encapsulate those implementation details, keep them hidden.

    Yes - though say "derived" rather than "child". :)

    I would just have each child object implement a report method that would instead return a common report object ...

    Something like that. It's difficult to comment on your specific
    example; you're familiar with it while I'm not.

    Dave

    Tuesday, December 01, 2015 12:03 PM
  • Your use case could be better solved by using composition instead of inheritance. Instead of trying to implement a concrete code construct around every variance of a vehicle you could imagine, which is a very inflexible, very high-maintenance, and not at all very reusable solution, implement a type of vehicle that is composed of these parts. This is very flexible and doesn't lead you into the sorts of problems you have with inheritance, namely having classes that inherit vestigial members that have no relevance only because some ancestor have that member and  the derived class is forced to take it on.

    class TireType { 
        public string Brand; 
        public string ModelNumber; 
    }
    
    class VehicleTire { 
        public TireType TireType; 
        public decimal Pressure; 
        public decimal TreadDepth;
    }
    
    class AxleType { 
        public decimal SomeAxleProperty;
        public decimal ImNoCarGuy;
        public string SoIHaveNoIdeaWhat;
        public string AnyoneWouldNeedToKnowAbout;
        public string AnAxle;
        public double Rose;
    }
    
    class VehicleAxle {
        public AxleType AxleType;
    }
    
    interface IFeature {
        public string Name { get; }
    }
    
    class PowerTakeOffFeature : IFeature {}
    
    class Vehicle {
        public ICollection<VehicleTire> Tires;
        public ICollection<VehicleAxle> Axles;
        public ICollection<IFeature> Features;
    
        public DateTime LastServiceDate;
    }
    If you notice this is very much how one might structure this data in a relational database.



    Tuesday, December 01, 2015 2:55 PM
  • >Your use case could be better solved by using composition instead of inheritance.

    Maybe, maybe not. The OP should be aware of both possibilities and
    choose whatever best fits their needs.

    https://www.thoughtworks.com/insights/blog/composition-vs-inheritance-how-choose

    Dave

    Tuesday, December 01, 2015 3:46 PM
  • While I agree that composition should not be the automatic answer over inheritance in every situation --every tool has its purpose -- I believe in this scenario it is warranted.

    The inheritance-based solution proposed would create much duplicate code, in addition to unnecessary interrogations of the instance to determine whether or not the instance actually utilizes a property or if it is simply a vestige of some ancestor that is out of context.

    Inheritance

    void DoStuff(Vehicle v, TireInspection ti) {
        ti.LeftFront = InspectTire(v.LeftFront);
        ti.RightFront = InspectTire(v.RightFront);
        ... etc.
    }
    
    Result InspectTire(Tire t) {
        if (t == null) {
            return;
        }
    
        ... do stuff
    
        return new Result();
    }

    At some point the developer may say, "Oh, all this repeated code. That's a code smell. Let's fix that:"

    void DoStuff(Vehicle v, TireInspection ti) {
        var properties = v.GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(p => typeof(Tire).IsAssignableFrom(p.PropertyType));
    
        foreach (var prop in properties) {
            var resultProperty = ti.GetType().GetProperty(prop.Name + "Result", BindingFlags.Public | BindingFlags.Instance);
            if (resultProperty != null) {
                var tire = (Tire)prop.GetValue(v);
                var result = InspectTire(tire);
                resultProperty.SetValue(ti, result);
            }
        }
    }
    
    Result InspectTire(Tire t) {
        if (t == null) {
            return;
        }
    
        ... do stuff
    
        return new Result();
    }

    Which is not a good solution because it makes assumptions about the names of properties, bypasses compile-time type checks, and disables for this section many productivity tools included in Visual Studio such as refactor. This is a runtime error waiting to happen.

    With composition you get the best of both worlds: Type safety and elimination of repeated code:

    void DoStuff(Vehicle v, TireInspection ti) {
        ti.Results = (
            from t in v.Tires
            select InspectTire(t))
            .AsEnumerable();
    
        // Or if results were a dictionary
    
        ti.Results = (
            from t in v.Tires
            select new {
                Key = t,
                Value = InspectTire(t),
            })
            .ToDictionary(o => o.Key, o => o.Value); 
    }
    
    Result InspectTire(Tire t) {
        ... do stuff
    
        return new Result();
    }



    Tuesday, December 01, 2015 5:24 PM