Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force a child class to initialise a parent property after computation

Tags:

c#

oop

I have a child class Bicycle that inherits from Agent. The agent has a property which depends on the bicycle to define it. Namely, the physics model for the agent needs to be initialised with the velocity and acceleration constraints which are defined on a per-bicycle-basis and would be different for another type of agent.

The problem I have is that I cannot pass the parameters I need to calculate (the velocity/acceleration require calculations to draw them from a theoretical distribution) for this in the base() constructor because of course the child class hasn't yet been instantiated.

The calculations are done once per bicycle instance but are used multiple times so a simple static method won't do the job. I can just call a protected method in the parent after they're calculated but AFAIK there's no way to enforce this in the child, or more particularly in any future children which I might not write.

So for example, I could:

public abstract class Agent
{
    protected IPhysics PluginPhysics { get; set; }

    protected Agent(...)
    {
    }
}

public class Bicycle : Agent
{
    private double maxA;

    public Bicycle(Object anotherParameter) : base(...)
    {
        maxA = ComputationOfMaxA();
        this.PluginPhysics = new Physics(anotherParameter, maxA);
    }

    private static double ComputationOfMaxA()
    {
       ...
    }
    ...
}

or I could:

public abstract class Agent
{
    protected IPhysics PluginPhysics { get; private set; }

    protected Agent(...)
    {
    }

    protected void SetupPhysics(Physics physics)
    {
        this.PluginPhysics = physics;
    }
}

public class Bicycle : Agent
{
    private double maxA;

    public Bicycle(Object anotherParameter) : base(...)
    {
        maxA = ComputationOfMaxA();
        SetupPhysics(new Physics(anotherParameter,maxA));
    }

    private static double ComputationOfMaxA()
    {
       ...
    }

    ...
}

I'd rather not do either of those as there's no compile-time way to ensure that the child initialises PluginPhysics that I can think of, and I'd rather PluginPhysics not be able to be changed once it's been initialised. I'd also rather not have the parts of the parameters that need to go into Physicsoutside the Bicycle class. I appreciate that all these things might not be simultaneously possible.

So short of strongly worded documentation or a bunch of run-time null checks in the parent class before any of the relevant class objects are called on, is there an obvious C#-ish way I'm missing of forcing a child to initialise a parent class field before use if you can't do it in the constructor?

like image 792
Smalltown2k Avatar asked Jun 06 '15 17:06

Smalltown2k


2 Answers

d4Rk's answer was very close, however you should try not call virtual methods from a constructor as bad things can happen. However if you use a combination of Lazy loading tricks and ISupportInitialize you can defer the creation of the plugin till after the constructor is finished.

public abstract class Agent : ISupportInitialize
{
    private bool _initialized = false;

    private IPhysics _pluginPhysics;
    protected IPhysics PluginPhysics 
    { 
        get
        {
            if(!_initialized)
                EndInit();
            return _pluginPhysics;
        }
    }

    protected Agent(...)
    {
    }

    protected abstract IPhysics CreatePhysics();

    ISupportInitialize.BeginInit()
    {
       //We make this a explicit implementation because it will not
       //do anything so we don't need to expose it.
    }

    public void EndInit()
    {
        if(_initialized)
            return;

        _initialized = true;
        _pluginPhysics = CreatePhysics();
    }
}

public class Bicycle : Agent
{
    private double maxA;
    Object _anotherParameter;

    public Bicycle(Object anotherParameter)
    {
        _anotherParameter = anotherParameter;
    }
    protected override IPhysics CreatePhysics()
    {
        ComputationOfMaxA();
        return new Physics(anotherParameter, maxA);
    }
}

The user of your class will need to call EndInit() after they get a object back to cause the IPhysics object to be created, however if they forget to call the initialize function the getter on the physics object will trigger the initialize call itself the first time it is used.

You could do everything I have shown without the ISupportInitialize interface and just having a public Initalize() method on the base class but I like to expose framework interfaces when they fit.

like image 192
Scott Chamberlain Avatar answered Oct 02 '22 19:10

Scott Chamberlain


What about enforcing the subclass to implement a CreatePhysics method, and call this in the base ctor?

Like this:

public abstract class Agent
{
    protected IPhysics PluginPhysics { get; private set; }

    protected Agent(...)
    {
        var physics = CreatePhysics();
        SetupPhysics(physics);
    }

    void SetupPhysics(IPhysics physics)
    {
        this.PluginPhysics = physics;
    }

    protected abstract IPhysics CreatePhysics();
}

public class Bicycle : Agent
{
    private double maxA;

    protected override IPhysics CreatePhysics()
    {
        ComputationOfMaxA();
        return new Physics(maxA);
    }
}
like image 34
d4Rk Avatar answered Oct 02 '22 21:10

d4Rk