0

I'm building an Unity extension that needs Scriptable objects. When using Scriptable objects the user must instantiate a class inheriting from ScriptableObject using ScriptableObject.createInstance(). So, if we need to supply more information to the instace to fully initialize it we're forced to use a 2 step intialization model.

I would like to know best practices when initializing an object using 2 steps (Creation using constructor and Initialization using Init() method).

The main problem I have found is that as you need to specialize Init() method in derived clases to accomodate initialization to the specialized object, you get more and more Init() methods because signatures do not match due to the different parameters used.

I have come across 2 solutions to this problem:

1) Hide parent methods and throwing an exception for the Init() methods that do not fully instantiate the specialized type.

2) Use a wrapper class that just exposes the valid Initialization methods.

Both of these seem cumbersome to me. And this is why I would like to hear how other people have overcome this problem.

An example of the problem for clarifying:

public class Class1 : ScriptableObject
{
    public virtual void Init()
    {
        Debug.Log("Class1::Init()");
    }
}

public class Class2 : Class1
{
    int myParameter;
    public virtual void Init(int parameter)
    {
        Debug.Log("Class3::Init()");
    }
}

//I could now do something like this. And the Class2 object
//Would contain an invalid state because myParameter wasn't initialized
Class2 c= ScritableObject.CreateInstance<Class2>();
c.Init();
Fattie
  • 27,874
  • 70
  • 431
  • 719
Notbad
  • 5,936
  • 12
  • 54
  • 100
  • 1
    You should do initialization to reasonable defaults in your constructor. – George Stocker Mar 12 '16 at 15:54
  • @GeorgeStocker: Sometimes this is not posible. Let's pretend you could do this always and have sensible and reasonable defaults after construction. The problem with init methods still remain. – Notbad Mar 12 '16 at 15:56
  • 2
    First of all, why do you plan on using 2-phase construction? What forces you to do so? If there is no such thing, then do not use it and use normal overloaded constructors. In C# you can even delegate constructors one to other `class Foo{ public Foo(int x, int y) : this(x) { .. } }` which usually saves you from writing init-like methods. But, if something forces you to use 2-phase construction, please include an explanation in the question, as it may impact the answers - i.e. must the Init be virtual? is it called by abstract base, by interface, by reflection? – quetzalcoatl Mar 12 '16 at 15:57
  • 2
    @Notbad What's your actual problem? – George Stocker Mar 12 '16 at 16:00
  • also I think that currently it's far more commoin to use (friend-)Factory (**very** simple to introduce) or Builder (a little more complex) or IoC (simple, but until got used to it feels to makes things upsidedown) pattern than "classic" 2-phase init. Wouldn't some of these be more appropriate? I don't want to force you to anything, but you see, all these specially designed language-builtin constructors chains and above mentioned patterns exist exactly to solve the problems you see with 2/or/N-phase construction.. – quetzalcoatl Mar 12 '16 at 16:02
  • thinking of which, I think that "more and more Init methods whose signatures don't match" seems like a good place for builder pattern. You essentially still have all those "init-like" methods, but they are in a separate class(es), they are named more reasonably, and after finishing the building, you get a clean object with no "init" garbage. – quetzalcoatl Mar 12 '16 at 16:05
  • 1
    @quetzalcoatl: The Main problem is that I'm using an external api (Unity engine) that forces to créate something called Scriptable objects through a static función ScriptableObject.createInstance(). So, I'm forced to initialize my own object that inherits from ScriptableObject in a second step. Previously I was happy using Construction chain. – Notbad Mar 12 '16 at 16:06
  • 1
    No, not toasted, sorry. Actually you still could externalize the initialization to a builder-like helper classes. Think of one parameterless Init() method that returns different objects: PersonInitializer, ShoeInitializer, BarInitializer, these objects are light and have only a reference to the original object instance, and they expose methods strictly related to initialization of that specific object type. Since all Init() are `X Init()`, they hide each other because C# doesnt overload over return types, and you can get all the variety in the returned hollow objects. It wont be compact though – quetzalcoatl Mar 12 '16 at 16:11
  • It seems something like my previous "wrapper solution". But I understand what you mean. Could work too. – Notbad Mar 12 '16 at 16:13
  • @Notbad You should include your actual problem in your question text; and why a normal constructor approach doesn't work for your problem so we can re-open this question and help you. – George Stocker Mar 12 '16 at 16:22
  • @GeorgeStocker: I have retagged the question and added a bit more of information about why I need this 2 step initialization model. But the core of the question remains the same. I was trying to simplifying things and trying to post a more general question not related to any game engine, tool or whatever – Notbad Mar 12 '16 at 16:40
  • 1
    It could be you misunderstand the fundamental difference between an ECS system which has the concept of a GameObject or Component, where **everything is a singleton**, and everyday OO programming. There's no meaningful sense in which you can "instantiate" a GameObject. It's an actual "thing" - it has to have a position and so on. ECS simply ***is not*** normal OO programming. – Fattie Mar 12 '16 at 16:52
  • 2
    @notbad the only way your question makes sense is in the context of a game engine (or some other external library that is already instantiated for you. That's why it needs to be in your question. – George Stocker Mar 12 '16 at 17:02
  • @JoeBlow: My question has nothing to do with ECS. ScriptableObject is just a regular class that handles serialization, etc... in the unity ecosystem. It is not even a component. You can think of it just as any other regular class in the context of my question. – Notbad Mar 12 '16 at 17:24
  • @GeorgeStocker: Don't want to be "that guy", but I think the core of the question that is: How to solve the problem I explained above when using a 2 step initialization model has nothing to do with game engines, etc... just with C# that has its particularities different for example from C++ on this subject. – Notbad Mar 12 '16 at 17:25
  • hi @notbad - fair enough, I'll take your word for it that I've subtly misunderstood the question. I'll just leave my (perhaps incorrect) comments there as it may help googlers. Just for your understanding, it is extremely common that people ask *exactly what I thought you meant* about Unity when they are experienced engineers having a go at Unity for the first time. Thus FYI I've answered & explained at vast length, as have most Unity engineers, what I ***thought you meant*** here many times - heh!!! – Fattie Mar 12 '16 at 17:44
  • I've removed the Unity tags from the question since it seems totally unrelated (again, if I do understand you). As always don't hesitate to further edit the tags, change back, edit your question, or whatever you wish. – Fattie Mar 12 '16 at 17:44
  • @NotBad you still haven't explained why you have to use a two step initialization model. Why you can't use the object's Ctor or create a factory to create the object with its defaults. – George Stocker Mar 12 '16 at 19:17
  • @GeorgeStocker: As I said prevoiusly the only way to créate an instance of the object I need is calling ScriptableObject.CreateInstance(). I can't build an object with new keyword. On the other hand, I could do the Factory thing, but the question isn't that. I have pointed out a problema I found and was just asking for good practices to solve it to learn from others. I will decide what route to go when I get all the information. – Notbad Mar 12 '16 at 21:49

0 Answers0