1

I have a java class with an init method. It's different from the constructor. The constructor just initializes the variables/fields. The init method connects to a database and performs some verifications on the database + it kick starts a background thread which handles incoming requests.

If you wanna think of it in pseudo-code:

class SomeService:

    ...
    queue
    other fields
    ...

    public SomeService(Constructor input):
        initializes fields...

    public void init():
        connect to database and verify certain existence of a table
        start a thread
        initialized = true

    public String doSomething(Some Input):
        if !initialized: throw exception
        add job to queue to be picked up by the running thread

    public Integer doSomethingElse(Some Other Input):
        if !initialized: throw exception
        add job to queue to be picked up by the running thread


My question is, is it better to force the object user to call init before using the class methods? If you check the code above, any method will throw exception if it's called before init.

Benefits:

  • This provides separation of concerns -- a method is only responsible of doing its job.

Downside:

  • This, however, makes a bad class usability: what if init failed? the object user will always has to handle this. And might even try to call init before every other class method call.

The other option is call init at the beginning of every class method, internally.

Benefits:

  • The class user doesn't need to worry about anything but getting his services done by the object.

Downside:

  • Now a method isn't as good in terms of separation of concerns. It does its job after it tries to initialize the object.

Please note that the initialization procedure is a must before any other method does its job. Other methods won't be functional unless the object is initialized by calling init.

I believe it's obvious why I separated init from the constructor:

  1. It's perfectly fine to have the object without it being initialized, and leave initialization for later.
  2. A constructor shouldn't take long for initialization, which isn't the case. Connection to database, running multiple statements, and starting a thread. This isn't as snappy as just setting fields values.
  3. If connection to database failed in a constructor, this will result in no object creation. This is bad behavior because the database can be available some other time after trial of object instantiation.

What I did is:

  • Called init at the beginning of every method. To get the benefit of that.
  • Availed init as public method to permit object caller for eager initialization.

So, what is it do you think is better? Call init at the beginning of every method? Leave it completely for the object user to handle it?

I tried to look it up but didn't find a fruitful answer.

I also tried to find in java 8 standard library if any class has init method, but couldn't find any. However, I found in java extension, Cipher class, which has init. and it works like approach#1 (leave it to the caller). That being said, this makes sense because its init takes user input, while mine doesn't.

I asked a chatgpt based bot and I got an answer that I should go with approach#2, calling init at the beginning of every method.

25
  • 1
    If you provide the init capability as part of a builder or factory method, then you'll get the benefit of the type system preventing use of the methods in the uninitialized state. The idea is to use two different types, one that carries the concept of uninitialized, and one that carries the concept of ready to use.
    – Erik Eidt
    Apr 20, 2023 at 17:48
  • 1
    by making init public you are forcing everyone to call init before any other methods in order to prevent possible no initialized exceptions. That's temporal coupling and is considered a code smell. So, why don't you call it from each other method? Why consumers must know about this temporal coupling? Does init needs to be triggered periodically (to perform checks)?
    – Laiv
    Apr 21, 2023 at 7:05
  • 1
    @Laiv, I'm availing init as public, but documenting the decoupling of init and the rest of the methods. If the user wants eager initialization, they can call init independently, otherwise, it's gonne be called internally if not initialized. So, the object user isn't forced or anything.
    – joker
    Apr 21, 2023 at 13:42
  • 1
    @Laiv, yeah I gave it all the context, and to my surprise, the answer (in fact, it was more of a conversation or a discussion if I may) was pretty consistent.
    – joker
    Apr 21, 2023 at 13:43
  • 1
    @joker why on earth does it matter? What problem are you trying to avoid by not having a method which both creates and initialises an object? i.e. why wouldn't it work or what problem would it cause? Apr 21, 2023 at 21:24

4 Answers 4

2

The init() method is a symptom, not the problem to be solved.

This class has some non-trivial initialization work to do. In a comment on another answer, you mentioned that a database connectivity hiccup shouldn't bring the whole thing crashing down. You need some robustness and fault tolerance in this class as well.

Since each method could throw an exception if the object is not initialized, callers must always test the object before calling a method. And when do they call init()? Can they call it more than once?

Laiv definitely has a good suggestion to hide this initialization logic. This does require a different design for the other methods. They need to work even when the database and background thread have not been initialized. You basically need an in-memory queue when the other methods are called before the object is fully initialized.

private void init():
    connect to database
    verify existence of a table
    start a thread
    initialized = true

    for params in doSomethingParams
        // replay method calls invoked before this object was initialized
        doSomething(params)

public String doSomething(Some Input):
    if !initialized
        init();
        doSomethingParams.add(Some Input);
    else
        someJobQueue.add(Some Input);

    return "what you would have normally returned";

Upon being initialized, loop through all of the "local" queues to push things into the newly initialized job queue. The point is, make sure this method succeeds even if the background thread or database is not available by pushing the parameters into a list. When those other resources are available, loop through that local list and call doSomething again, passing the parameters in the list, which essentially "replays" past method calls.

The main challenge here is that any call to your service can initiate an asynchronous operation, but if you are pushing things onto a queue that another thread uses, it's already asynchronous. Callers should assume this fact.

You can keep the init() method, but it would be private, and only methods in the service class will be calling it. This gives you lazy initialization, eliminates some unnecessary exceptions, and gives you the robustness you seek.

6
  • This is a pretty new angle to the matter .. one way - new one - to look at it. Thank you! From your comment, I believe you fully understood the problem. Now, let me think this out loud with you. You're proposing that I whether the object is initialized or not, other methods can succeed by adding a capture of their call parameters, so that when the object does get initialized, we can go over the captured calls and parameters and execute them.
    – joker
    Apr 21, 2023 at 19:46
  • I think this will result in a false sense of success to the object caller; because requests will now apparently succeed (won't throw exceptions), yet the action requested might not get executed at all, if the object doesn't get initialized throughout the lifetime of the application. Or it can still throw an error, yet failed requests will, surprisingly, get executed at a later point in time.
    – joker
    Apr 21, 2023 at 19:48
  • To be honest, your suggestion looks solid, especially, the methods provided by the service are all asynchronous. So, calls - assuming the object is initialized - are always successful. The object caller passes in [optionally] a consumer (or a callback) to be called when the request is handled so that the object caller can handle scenarios of failure and/or success. So, your suggestion actually is still good.
    – joker
    Apr 21, 2023 at 19:51
  • @joker: you got it. If the caller needs to handle error scenarios, you either need to support a callback, or some downstream process needs to send a message back to some component notifying them of the failure. Apr 21, 2023 at 20:14
  • It would really work best without a callback. The downstream component should be able to message the caller somehow notifying them the job is done and indicate any failures. I'm not sure about the use case or architecture, but if this is a micro services environment, designing for eventual consistency should help. Apr 21, 2023 at 20:16
3

I'm availing init as public, but documenting the decoupling of init and the rest of the methods. If the user wants eager initialization, they can call init independently, otherwise, it's gonna be called internally if not initialized. So, the object user isn't forced or anything

The if sounds like you don't have use cases (or requirements) backing the decision of making init public. You made an assumption but looks like you haven't proved it to be true, so YAGNI applies. At the moment.

Note that we don't know if you are implementing a library or a piece of a framework. If that's the case, the more options you give the more use cases you cover. But having many options is not always better. The proof? Here you are with a list of pros and cons and trade-offs. Too many options can be confusing (which one is best? Which one do I need? I have to deal with trade-offs and I hate to do that, what if...).

I would suggest keeping it simple. Hide the method because it makes the consuming code likely to be cleaner, less verbose and more loosely coupled. Later, you can find other ways to implement eager initialization. I like @ErikEidt suggestion about doing so from the instance provider (factory, builder, IoC container, etc). The goal is to keep this "complexity" away from the consumer and located in a single place of failure (or source of truth depending on how you look at it).

5
  • You are correct, for the most part. It's true that I don't have a use case or requirement. So, nice catch. I have to give you that. You are also correct about not providing too many options. And yes, I'm building sort of a library to be used in the main application.
    – joker
    Apr 21, 2023 at 16:38
  • I don't like, however, the idea of factory or builder here. The problem doesn't fit in any of the two patterns. Normally, factories and builders are for pojos (objects construction i.e., instantiation and setting variables). In my case, the object, in terms of construction is perfectly fine without initialization. This is strictly in terms of construction/instantiation and not initialization. Maybe think of a Thread. You can instantiate one, but it's almost not usable until you call start
    – joker
    Apr 21, 2023 at 16:40
  • Plus we have to factor in that the database which the service performs checks against might be down at some times. We need only to fail requests that happen to be invoked when the database is down. If we only allow init to be called upon instantiation, and the database happens to be down at the time but brought back up at later point int time, we risk losing all future requests.
    – joker
    Apr 21, 2023 at 16:43
  • 1
    If the component is indeed like a Thread then it's created every time and the init seems reasonable. If it's a single instance you might be interested in frequent readiness checks in order to handle denials of service gracefully. In this case you handle the availability from different components while keeping consumers agnostic to these details.
    – Laiv
    Apr 21, 2023 at 23:12
  • Would you please elaborate more on "If it's a single instance you might be interested in frequent readiness checks in order to handle denials of service gracefully. In this case you handle the availability from different components while keeping consumers agnostic to these details."? I didn't quite catch that.
    – joker
    Apr 25, 2023 at 12:47
2

I would recommend avoiding init-methods if at all possible. Getting an "object not initialized" is annoying, since it occurs at runtime. Moving errors to compile time makes types much easier to use, since your IDE should be able to tell you about problems even before you explicitly compile the code.

Using a "lazy initialization" pattern is preferable to forcing the user to call initexplicitly. But it still have the problem that any runtime errors may occur long after the class is created, and that might not be ideal since any incorrect parameters might not be apparent at that point.

Another option is to split the class into two:

  1. A "factory" class with a single method that does any initialization and returns a service object.
  2. The actual service type with all other methods. Once this has been created any initialization is complete, and the object ready to use.

This makes the initialization step visible to the user, while still ensuring it is done before the service is used.

3
  • Thank you for your solid response! Good argument. I don't like, however,,the idea of factory or builder here. The problem doesn't fit in any of the two patterns. Normally, factories and builders are for pojos (objects construction i.e., instantiation and setting variables). In my case, the object, in terms of construction is perfectly fine without initialization. This is strictly in terms of construction/instantiation and not initialization. Maybe think of a Thread. You can instantiate one, but it's almost not usable until you call start.
    – joker
    Apr 21, 2023 at 16:45
  • Plus we have to factor in that the database which the service performs checks against might be down at some times. We need only to fail requests that happen to be invoked when the database is down. If we only allow init to be called upon instantiation, and the database happens to be down at the time but brought back up at later point int time, we risk losing all future requests.
    – joker
    Apr 21, 2023 at 16:45
  • 1
    @Joker these are just general suggestions. Using a "factory" can be useful in some circumstances, that might or might not include your circumstances.
    – JonasH
    Apr 24, 2023 at 9:01
1

An option to consider would be Lazy Initialization. With lazy init, we check to see if some value exists before we use it and if it doesn't, we take the time to spin it up. Really common on properties. In your case, the init could be tucked behind some private prop with any needed values provided to the constructor. It also avoids delaying construction and allows you to provide additional logic to handle re-tries, connectivity, or other work only when actually needed.

ex:


public class Foo {
    DbConn conn;
    private String connString;

    public Foo(String connString) {
        this.connString = connString;
    }

    DbConn getConn() {
        if (conn == null) conn = initDbConn();
        return conn;
    }

    private DbConn initDbConn() {
        return new DbConn(connString);
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.