Inheritance and Constructor of abstract class

Hello,
I'm trying to implement an Interaction system using the strategy pattern and I have a small issue.
I have an Item class that stores an Interaction and I want the children of Item to instantiate the Interaction to the type of interaction that fits them. I would usually modify the parent's constructor which forces the child to implement it in it's own constructor, but I found out that I shouldn't be using constructors in monobehavior scripts.
I have a work around in the code below, but I wanted to ask if there is a better way of doing this.
Thanks for your help in advance.

Code:

// Parent class
public abstract class Item : MonoBehaviour
{
protected IInteraction interaction;

protected abstract void setInteractionType();
public void interact(GameObject go)
{
interaction.Interact(go);
}

}

// Child class
public class Food : Item
{
// food can be picked up
protected override void setInteractionType()
{
interaction = new PickUp();
}

void Start()
{
setInteractionType();
}
}

If you post a code snippet, ALWAYS USE CODE TAGS:

How to use code tags: https://discussions.unity.com/t/481379

[quote=“The-Magician”, post:1, topic: 918344]
if there is a better way of doing this.
[/quote]

Solutions are not better or worse. They are engineered for a particular context to solve a particular problem.

If the above solves your problem, you’re done. Move on.

Personally I would NEVER touch abstract / inheritance in Unity for these reasons:

  • they don’t solve the problems I tend to encounter in gamedev.

  • In my experience, inheritance leads to extremely brittle codebases that are almost impossible to refactor without completely rewriting them

  • Unity already uses a Component architecture, so composition is favored over inheritance.

  • Unity plays very nicely with interfaces (technically a form of inheritance, but I don’t think of it like that; it’s contractual)

  • 90%+ of all Unity engineers don’t EXPECT inheritance and thus will never do things like base.Awake() or base.Start(), or else they tend to forget to do them. This means you are setting your code up for likely future failures.

Using Interfaces in Unity3D:

https://discussions.unity.com/t/797745/2

https://discussions.unity.com/t/807699/2

Check Youtube for other tutorials about interfaces and working in Unity3D. It’s a pretty powerful combination.

1 Like

[quote=“Kurt-Dekker”, post:2, topic: 918344]
If you post a code snippet, ALWAYS USE CODE TAGS:

How to use code tags: https://discussions.unity.com/t/481379

Solutions are not better or worse. They are engineered for a particular context to solve a particular problem.

If the above solves your problem, you’re done. Move on.

Personally I would NEVER touch abstract / inheritance in Unity for these reasons:

  • they don’t solve the problems I tend to encounter in gamedev.

  • In my experience, inheritance leads to extremely brittle codebases that are almost impossible to refactor without completely rewriting them

  • Unity already uses a Component architecture, so composition is favored over inheritance.

  • Unity plays very nicely with interfaces (technically a form of inheritance, but I don’t think of it like that; it’s contractual)

  • 90%+ of all Unity engineers don’t EXPECT inheritance and thus will never do things like base.Awake() or base.Start(), or else they tend to forget to do them. This means you are setting your code up for likely future failures.

Using Interfaces in Unity3D:

https://discussions.unity.com/t/797745/2

https://discussions.unity.com/t/807699/2

Check Youtube for other tutorials about interfaces and working in Unity3D. It’s a pretty powerful combination.
[/quote]

Will make sure to use TAGS next time. I started learning unity a week ago so I’ll try to get into the composition side of things instead of inheritance. Thank you for the insight!

1 Like

You can actually use a constructor for this particular use case just fine, and achieve better encapsulation.

// Parent class
public abstract class Item : MonoBehaviour
{
    private readonly IInteraction interaction;

    protected Item(IInteraction interaction)
    {
        this.interaction = interaction;
    }

    public void Interact(GameObject go)
    {
        interaction.Interact(go);
    }
}
// Child class
public class Food : Item
{
    public Food() : base(new PickUp())
    {

    }
}

You can define constructors in MonoBehaviour-derived classes, but you just can't call them manually from external classes, nor pass any arguments to them from the outside. So all non-abstract classes should always have a public parameterless constructor.

The constructor also gets executed before Unity's deserialization process, so you shouldn't ever use constructors to initialize serialized fields, as those values would get overridden.

1 Like

And yeah I agree that over-using inheritance can lead to very brittle code. If you have deep and complicated inheritance trees, then it can become really easy to accidentally break something in one of the many derived classes when you make any change in a base class.

Using virtual methods in particular can get really nasty over time:

public override void Setup()
{
    // must initialize interaction before calling base.Setup,
    // or a null reference exception will be thrown!
    interaction = new PickUp();

    // must call base.Setup before LateSetup,
    // or a null reference exception will be thrown!
    base.Setup();

    interaction.LateSetup();
}
1 Like

Thank you!

1 Like