Mark 909
Mark 909

Reputation: 1835

Object Orientated Design Parent / Child Relationship

This is a general best practice question about creating parent / child relationships with objects.

Let's say I have Wheel and Car objects and I want to Add a Wheel object to car object

public class Car{

    private List<Wheel> wheels = new List<Wheel>();

    void AddWheel ( Wheel WheelToAdd)
        {
            wheels.Add(WheelToAdd)
            //Some Other logic relating to adding wheels here
        }
    }
}

So far so good. But what if I want to have a Car property of my wheel to say which parent car it relates to. Something like this

 public class Wheel {

     private Car parentCar;
     public Car 
     {
        get
        {
         return parentCar
        }

  }

}

When adding the wheel to the car, at which point would you set the parent property of the Wheel? You could set it in the Car.AddWheel method, but then the Car Property of the Wheel object would have to be Read/Write and then you could set it outside of the AddWheel method, creating an inconsistency.

Any thoughts, many thanks in advance

Upvotes: 7

Views: 7062

Answers (8)

Charles Bretana
Charles Bretana

Reputation: 146469

A better design methodology, (Domain Driven Design) specifies that you should first decide what the domain model requirements are for these entities... Not all entities need to be independantly accessible, and if Wheel falls into this category, every instance of it will always be a child member of a Car object and you don't need to put a Parent property on it... Car becomes what is referred to as a root entity, and the only way to access a Wheel is through a Car object.

Even when the Wheel object needs to be independantly accessible, the domain model requirements should tell you what the usage patterns require. Will any Wheel ever be passed around as a separate object, without it's parent ? In those cases is the Car parent relevant? If the identity of the parent Car is relevant to some piece of functionality, why aren't you simply passing the complete composite Car object to that method or module? Cases where a contained composite object (like a Wheel) must be passed on it's own, but the identity of the parent (the object it is part of) is needed and/or relevant, are in fact not a common scenario, and approaching your design using the above type of analysis can save you from adding unnessary code to you system.

Upvotes: 8

ComeIn
ComeIn

Reputation: 1609

May be a little left of centre but conceptually a wheel is a "part" of a car. Did you consider using partial classes? A passenger is a "part" of a queue. I find the use of partial classes and well defined interfaces is very useful in some scenarios. You can abstract out aspects of whole object into interfaces and implement each interface as a partial class. The result is a complete object that is able to be abstracted by its partial interface to meet an array of contractual scenarios.

Upvotes: 0

EricBoersma
EricBoersma

Reputation: 1019

I'm going to go with "Don't". A wheel isn't just a property of a Car. A wheel could be used on a Trailer or a Cart or a FerrisWheel (ok, maybe that's a bit of a stretch). The point is, by making the car which uses the wheel a property of the wheel itself, you couple your wheel's design to being used on a car, and at that point, you lose any reusability for the class.

In this case, it seems like there's a lot to lose and little to gain by letting the wheel know what it's actually used on.

Upvotes: 0

code4life
code4life

Reputation: 15794

Question: Can the Wheel instance be assigned to a different Car instance later in time?

1) If yes, the expose a public method to set the parentCar. You could make it a fluent method, for ease of code:

public class Wheel 
{
    private Car parentCar;

    public ParentCar 
    {
        get
        {
            return parentCar;
        }
    }

    public void SetParentCar(Car _parentCar)
    {
        parentCar = _parentCar;
        return this;
    }
}

Where you assign add the wheel, make the assignment:

void AddWheel ( Wheel WheelToAdd)
{
    wheels.Add(WheelToAdd.SetParentCar(this));
    //Some Other logic relating to adding wheels here
}



2) If not, just set the parent in the constructor.

public class Wheel 
{
    private Car parentCar;

    public ParentCar 
    {
        get
        {
            return parentCar;
        }
    }

    public Wheel(Car _parentCar)
    {
        parentCar = _parentCar;
    }
}

Upvotes: 0

Bryan Watts
Bryan Watts

Reputation: 45445

Bidirectional relationships tend to be very difficult to implement correctly. The prevailing advice here should be "don't do that, most likely you don't actually need it, and it will do you more harm than good."

If, after much careful consideration, you decide a bidirectional relationship is warranted, you can make the setter of the Car property internal, which doesn't fully protect against rogues setting unwanted values but it does limit the surface area significantly.

Upvotes: 4

Steve Townsend
Steve Townsend

Reputation: 54138

It makes sense to me for the Car property of the Wheel object to be writable because there's nothing to stop you moving a Wheel from one ToyotaCamry (subclass of Car) to another. Though you might want to have a Drivable property on the original car to check that List.Count is still 4 before allowing Car.Drive() to be called.

Do you create Wheels with Cars? if so set the property at that time. Otherwise set it when Wheel is attached to Car.

Upvotes: 0

mway
mway

Reputation: 4392

You might want to consider having a setter that only sets Wheel.parentCar if that setting is null, but only if you're able to assume that your first setting will be valid and are thus able to disregard any other attempts.

Edit: but yes, that would be the proper place to add the Car object. You could also do a check such that you create (for example) Wheel.validateCar(Car carInQuestion), where it enforces that the parentCar property is only set where the current Wheel object exists in Car. This implies that you would have a public method for searching Car.wheels for membership based on a particular instance of a wheel. But that's only if you really feel the need to be strict.

Upvotes: 2

Related Questions