Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We’re making changes to the Unity Runtime Fee pricing policy that we announced on September 12th. Access our latest thread for more information!
    Dismiss Notice
  3. Dismiss Notice

Question Inheritance and Constructor of abstract class

Discussion in 'Scripting' started by The-Magician, May 20, 2023.

  1. The-Magician

    The-Magician

    Joined:
    Dec 10, 2022
    Posts:
    4
    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();
    }
    }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,077
    If you post a code snippet, ALWAYS USE CODE TAGS:

    How to use code tags: https://forum.unity.com/threads/using-code-tags-properly.143875/

    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://forum.unity.com/threads/how...rereceiver-error-message.920801/#post-6028457

    https://forum.unity.com/threads/manager-classes.965609/#post-6286820

    Check Youtube for other tutorials about interfaces and working in Unity3D. It's a pretty powerful combination.
     
    The-Magician likes this.
  3. The-Magician

    The-Magician

    Joined:
    Dec 10, 2022
    Posts:
    4
    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!
     
    Kurt-Dekker likes this.
  4. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,088
    You can actually use a constructor for this particular use case just fine, and achieve better encapsulation.
    Code (CSharp):
    1. // Parent class
    2. public abstract class Item : MonoBehaviour
    3. {
    4.     private readonly IInteraction interaction;
    5.  
    6.     protected Item(IInteraction interaction)
    7.     {
    8.         this.interaction = interaction;
    9.     }
    10.    
    11.     public void Interact(GameObject go)
    12.     {
    13.         interaction.Interact(go);
    14.     }
    15. }
    Code (CSharp):
    1. // Child class
    2. public class Food : Item
    3. {
    4.     public Food() : base(new PickUp())
    5.     {
    6.  
    7.     }
    8. }
    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.
     
    The-Magician likes this.
  5. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,088
    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:
    Code (CSharp):
    1. public override void Setup()
    2. {
    3.     // must initialize interaction before calling base.Setup,
    4.     // or a null reference exception will be thrown!
    5.     interaction = new PickUp();
    6.  
    7.     // must call base.Setup before LateSetup,
    8.     // or a null reference exception will be thrown!
    9.     base.Setup();
    10.  
    11.     interaction.LateSetup();
    12. }
     
    The-Magician likes this.
  6. The-Magician

    The-Magician

    Joined:
    Dec 10, 2022
    Posts:
    4
    Thank you!
     
    SisusCo likes this.