0

It's a pretty simple question really, how should one properly declare and use a global resource without violating Java's OOP principles?

When I'm writing in C, I would declare a struct in global space. This could then be accessed by any function that needs its data; it could be database connection details, or socket information, something needed by all, but belonging to no one.

When working in Java threads, I'm finding that each instance of the worker thread needs that connection struct details, but if the manager thread doesn't have access, it can't pass it as a value in the constructor.

I could make the class static, but that feels like I'm cheating and introducing bad code into the design. I'm sure there must be a "correct" way of handling this in Java, but as a primarily C developer, my mind is more used to the idea of globals.

8
  • A static variable in a class is a global and can be used as such. The modern alternative is to use a dependency injection framework. Nov 9, 2020 at 15:51
  • 2
    Why can't the manager thread pass it as a value in the constructor? Explain.
    – user253751
    Nov 9, 2020 at 16:08
  • @user253751 because the manage thread is several steps down from the initial thread and there might be several such classes needing passing so id be passing several classes by value through multiple constructors instead of having the thread directly access the needed data.
    – Skeith
    Nov 9, 2020 at 16:18
  • Explain the several steps.
    – user253751
    Nov 9, 2020 at 16:27
  • 1
    @ThorbjørnRavnAndersen Why, that's curiously exactly what I do! One of the things, at least. And when over half of my java codebase's bugs are coming from issues caused by the dependency injection framework masking out errors or made worse by the DI framework making it a hell to debug code, I can say safely that no - it isn't helping me at all.
    – T. Sar
    Nov 10, 2020 at 14:25

2 Answers 2

1

First, I think you are misinterpreting the avoidance of global state as a Java principle. There's nothing about having static methods or state that violates anything in Java. This kind of thing is not uncommon and you can find many examples of it in the most common core libraries e.g. System.out.

The avoidance of global state (e.g. Singletons) is something that is a later idea and one that stems from enhancing reuse and testability of code. The main thing here is that once you have declared something as global state and base a lot of dependencies on it the design become brittle. A more flexible design avoids having to know where to get their dependencies.

There's really nothing stopping you from creating a single static getConnectionParameters method. It will work just fine but there are downsides as mentioned above. As a pragmatist, I would suggest that you balance the effort to do something more sophisticated with the benefits. You might need to try it and see the downsides. But the Java police will not show up and read you your rights if you use static state or methods. Personally, I think the concerns are a little overblown.

Here are a few other things that might help you navigating the Java landscape here:

  • Don't pass connection parameters to objects so that they can establish connections on their own, use a connection pool.

  • Avoid sharing database connections across threads. Some drivers can handle this but it's not something I would count on in general.

  • I'm not sure you need this here but ThreadLocal is a very useful tool when doing multi-threading. A single instance can hold a different value for each thread automatically. It works basically like a dictionary (Map in java terms) keyed by Thread.

  • ThreadFactory is another helpful threading abstraction. It's often the only way you can control how threads are created in frameworks.

Having said all that, I don't really see what the issue is with passing "values down the whole thread chain". I think maybe you see this as problematic because you are thinking in terms of 'structs'. All you need to do is pass a reference to a connection pool along. If you have other similar common resource instances, you could bundle them into a single object as well and pass a reference to that along

0

You think about which objects need access to which information.

You have worker threads which need the connection details to connect the database. Okay. So you pass those in the constructor of the worker thread.

You have a manager thread which creates worker threads. Okay. So you pass the connection details in the constructor of the manager thread so you can pass them to the constructors of the worker threads.


Now, you will say you have too many things to pass through too many constructors.

Well, you don't have to pass the actual connection details. The manager thread needs to create worker threads, but it doesn't really care about the details, right? So you may find it more convenient - instead of passing all the worker thread parameters - to just pass a factory object.

// abstract class or interface.
// In this particular case you could also use java.util.function.Supplier<WorkerThread>
public abstract class WorkerThreadFactory {
    public abstract WorkerThread createWorkerThread();
}

// and create the manager like this
ManagerThread manager = new ManagerThread(new WorkerThreadFactory() {
    @Override
    public WorkerThread createWorkerThread() {
        return new WorkerThread(databaseConnectionDetails, otherStuffItNeeds);
    }
});

// short version with a lambda expression
// note: I think this only works if WorkerThreadFactory is an interface
ManagerThread manager = new ManagerThread(() -> new WorkerThread(databaseConnectionDetails, otherStuffItNeeds));

Now, if you add more parameters to the worker threads, you don't need to update the manager class.

You could even go a step further and use this for the worker threads as well:

// instead of
new WorkerThread(hostname, port, username, password, databaseName)

// you could use
new WorkerThread(() -> connectToDatabase(hostname, port, username, password, databaseName))

// and now you don't have to pass 5 separate pieces of information into WorkerThread

Of course, I can't be more specific without seeing your code.


Yes, you could use static fields. Static fields are a cop-out for when you can't figure out how to structure your code this way. It's still bad design even if it's easy. (note that there's no problem with static methods, only static fields)

And this also applies to C. Global variables in C are considered bad, for the same reason static fields in Java are considered bad. One day you will want two managers connected to different databases and you will have to do it the hard way anyway.

Your Answer

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

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