Why Use an Initialization Method Instead of a Constructor

Why use an initialization method instead of a constructor?

Since they say "timing", I guess it's because they want their init functions to be able to call virtual functions on the object. This doesn't always work in a constructor, because in the constructor of the base class, the derived class part of the object "doesn't exist yet", and in particular you can't access virtual functions defined in the derived class. Instead, the base class version of the function is called, if defined. If it's not defined, (implying that the function is pure virtual), you get undefined behavior.

The other common reason for init functions is a desire to avoid exceptions, but that's a pretty old-school programming style (and whether it's a good idea is a whole argument of its own). It has nothing to do with things that can't work in a constructor, rather to do with the fact that constructors can't return an error value if something fails. So to the extent that your colleagues have given you the real reasons, I suspect this isn't it.

Is it better to make a separate constructor and init method, or to create a constructors with parameters?

One clearcut advantage of doing the initialization work inside constructors is that the user of your class doesn't need to explicitly invoke init methods, and it avoids issues related objects not fully initialized. And in case you need to extend your class with some other class your initialization related code will get invoked appropriately.

I think what you can do is, have one init method that deals with all the initialization related code in private/protected scope and then call this method from the different constructors depending on different scenerios.

Arguments against initialize() method instead of constructors

Both single step (constructor) initialisation and two step (with an init method) initialisation are useful patterns. Personally I feel that excluding either is a mistake, although if your conventions prohibit use of exceptions entirely then you prohibit single step initialisation for constructors that can fail.

In general I prefer single step initialisation because this means that your objects can have
stronger invariants. I only use two step initialisation when I consider it meaningful or useful for an object to be able to exist in an "uninitialised" state.

With two step initialisation it is valid for your object to be in an uninitialised state - so every method that works with the object needs to be aware of and correctly handle the
fact that it might be in an uninitialised state. This is analogous to working with pointers, where it is poor form to assume that a pointer is not NULL. Conversely, if you do all your initialisation in your constructor and fail with exceptions than you can add 'the object is always initialised' to your list of invariants, and so it becomes easier and safer to
make assumptions about the state of the object.

why constructor use, why not always use simple initialization of variables?

Simple definition of constructor:

Special methods that initialize an object of a class. Always and only used together with the new keyword to create an instance of a class.

  1. Has the same name as the name of the class.
  2. Can take one or more arguments.
  3. Has no return value, not even void.
  4. Default constructor takes no parameters.
  5. More than one constructor exist (method overloading).

but why use constructor initialization if it automatically done by
compiler !

Constructors initialize by the compiler (default constructor) if you have not implemented a constructor.

So why do we need to implement a constructor?

  • Its job is to tell all of the local variables their initial values,
    and possibly to start off another method within the class to do
    something more towards the purpose of the class.
  • Depends on what data you have, i.e. what is available.
  • Different ways of creating an object.
  • All class variables have to be initialized with the constructor.

As a example:

  • Consider the Rectangle class in the java.awt package which provides several different constructors, all named Rectangle(), but each with a different number of arguments, or different types of arguments from which the new Rectangle object will get its initial state. Here are the constructor signatures from the java.awt.Rectangle class:

  • public Rectangle()

  • public Rectangle(int width, int height)
  • public Rectangle(int x, int y, int width, int height)
  • public Rectangle(Dimension size)
  • public Rectangle(Point location)
  • public Rectangle(Point location, Dimension size)
  • public Rectangle(Rectangle r)

What if your member variables are private (security reasons)? If you do not want to give other classes to handle member variables, you have to use getters and setters, but in the first place, you can initialize it with a constructor, then you can change it using getters and setters later on when you need to change/update it.

Why constructor is used instead of functions?

The most important difference: When you instantiate an object it's constructor will be invoked whereas calling a method is always optional. You therefore might forget to call your initialization method and fail to initialize everything correctly.

For example, all these normal ways of instantiating an object will call the constructor

Foo* p = new Foo();
Foo p;

Or if you have mandatory arguments, don't define a default constructor and instead require construction with parameters:

class Foo
{
private:
Foo();
public:
Foo(int param1, double param2)
};

This has the advantage of requiring the arguments before you even instantiate the class. So you're forced to do:

Foo* p = new Foo(1, 5.0);

and failing to construct with valid arguments becomes a compiler error:

Foo* p = new Foo(); // compiler error

So whenever possible, always err on the side of doing your initialization in a constructor. There are a few cases where a constructor might not be viable. For example, the only way to fail a constructor is by using an exception. The failure to construct may be "routine" and not truly exceptional. Also exceptions might be expensive on some architectures. Another case might be when you want to ensure that virtual methods are fully bound, which is guaranteed to be true only after construction.

Use of Initializers vs Constructors in Java

Static initializers are useful as cletus mentioned and I use them in the same manner. If you have a static variable that is to be initialized when the class is loaded, then a static initializer is the way to go, especially as it allows you to do a complex initialization and still have the static variable be final. This is a big win.

I find "if (someStaticVar == null) // do stuff" to be messy and error prone. If it is initialized statically and declared final, then you avoid the possibility of it being null.

However, I'm confused when you say:

static/instance initializers can be used to set the value of "final"
static/instance variables whereas a constructor cannot

I assume you are saying both:

  • static initializers can be used to set the value of "final" static variables whereas a constructor cannot
  • instance initializers can be used to set the value of "final" instance variables whereas a constructor cannot

and you are correct on the first point, wrong on the second. You can, for example, do this:

class MyClass {
private final int counter;
public MyClass(final int counter) {
this.counter = counter;
}
}

Also, when a lot of code is shared between constructors, one of the best ways to handle this is to chain constructors, providing the default values. This makes is pretty clear what is being done:

class MyClass {
private final int counter;
public MyClass() {
this(0);
}
public MyClass(final int counter) {
this.counter = counter;
}
}

Should I put a lot of initialization work in constructor or in a member method?

I really like the following way of looking at this, but, as mentioned at the bottom of this post, it has a single, massive, drawback:

The constructor's job is to establish the class invariant.

A class invariant is the description of the legal state a class can be in in between calls to public member functions, and each and every public member function should be well-behaved for any and all possible invariant state. So it's always a good idea to keep your class invariants as strict as possible.

In light of all this: the amount of work being done in a constructor should be whatever is needed to set up as strict an invariant as reasonably possible for the class.

For example:

If I have a class that holds a pointer to an OS window:

class Window {
native_window* w_;
...
};

The class invariant could be "w_ is always either null, or points to an instance of a window.", but that's annoying, since it means that most member functions will have to perform a null check. If the invariant was "w_ points to a valid window handle", then you can rely on that assumption and greatly simplify the rest of the class.

However! This means that should the constructor fail to establish an invariant (the OS call to create the window failed because of reasons), then the only way you can handle the failure is to throw an exception. So if you are operating in a codebase where exceptions are not permitted, then this whole approach to class design is moot.

Solidity: Why use Initialize function instead of constructor?

The initialize you wrote as an example is wrong, because it could be called by the owner several times, the purpose of the initializer (as well as the constructor) is to be called as the first function before using the contract, and never be called back a second time

However, initialize is used instead of the constructor when a contract that uses a proxy is published

Why?

In Ethereum, there are three major types of contract calls: regular CALL, STATICCALL, and DELEGATECALL.

When contract A makes a CALL to contract B by calling foo(), the function execution relies on contract B’s storage, and the msg.sender is set to contract A.

This is because contract A called the function foo(), so that the msg.sender would be contract A’s address and msg.value would be the ETH sent along with that function call. Changes made to state during that function call can only affect contract B.

Example of call

However, when the same call is made using DELEGATECALL, the function foo() would be called on contract B but in the context of contract A. This means that the logic of contract B would be used, but any state changes made by the function foo() would affect the storage of contract A. And also, msg.sender would point to the EOA who made the call in the first place.

Sample Image

how can we handle the constructor logic? The contract’s constructor is automatically called during contract deployment.

But this is no longer possible when proxies are in play, as the constructor would change only the implementation contract’s storage (Contract B), not the storage of the proxy contract (Contract A), which is the one that matters.

Therefore, an additional step is required. We need to change the constructor in a regular function. This function is conventionally called initialize or init, this function will be called on the proxy contract once both contracts have been published, so as to save all changes of state on the proxy contract (contract A) and not on the implementation (contract B )



Related Topics



Leave a reply



Submit