The problem is a design problem in Object-Oriented Programming that I'm trying to find or think of the best solution for, And not sure of the solutions I have thought of so far.
The problem:
I wrote a class called ComponentsHub
whose idea is to store within it all the main objects that are essential for running the program. Each object is static, final, and public. I use a public access modifier to make it easy to access an object via static import or just to access it statically.
package com.rtransfer.net.components;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import com.rtransfer.net.system.StorageManager;
public class ComponentsHub {
public static final Logger logger;
public static final Server server;
public static final StorageManager storageManager;
public static final SecurityManager securityManager;
public static final RequestForwarder requestForwarder;
public static final Authenticator authenticator;
public static final Uploader uploader;
public static final ConnectionHandler connectionHandler;
public static final Listener listener;
static {
logger = Logger.getLogger("rtransfer.net");
try {
FileHandler handler = new FileHandler("logs/logs.txt");
logger.addHandler(handler);
} catch (SecurityException | IOException e) {
e.printStackTrace();
}
server = new Server();
storageManager = new StorageManager();
securityManager = new SecurityManager();
requestForwarder = new RequestForwarder();
authenticator = new Authenticator();
uploader = new Uploader();
connectionHandler = new ConnectionHandler();
listener = new Listener();
}
}
It's pretty obvious that there are some issues with this code/design (Hope you can tell me some of them). For example not separating concerns. I do not feel comfortable with this architecture so much, I think I have created some kind of a "God Class".
Solutions I thought of:
Create classes that will build these objects, such as Builders or Factories, and so I will remove from ComponentsHub the responsibility for the construction and initialization of these objects.
Create classes that will centralize these objects in a more categorical way. Or in other words, create sub-ComponentsHubs.
I know this is a pretty common type of problem in designing object-oriented systems. I want to solve this so that I do not have to live in nightmares in the further development of my software.
Are the solutions I proposed solves part of the problem? Are there alternatives? I would be happy if you would recommend me suitable design patterns or other techniques that will allow me to solve this problem. How and where should I create objects and allow fairly simple access to them?
CodePudding user response:
However you structure this singleton object, it remains a singleton (or collection of singletons). It can create unexpected interdependencies between your classes, and makes them difficult to test or reason about in isolation.
The magic phrase to put into a search engine is "dependency injection". That term comes with a lot of baggage and people sometimes associate it with complicated frameworks, but at the core, the principle is really simple: rather than reach out and find its dependencies autonomously, each class should require that the code using the class provides the dependencies.
Not this:
class Dog {
void bark() {
SoundManager.emitSound("woof");
}
}
But this:
class Dog {
private final SoundManager soundManager;
public Dog(SoundManager soundManager) {
this.soundManager = soundManager;
}
void bark() {
this.soundManager.emitSound("woof");
}
}
And this principle goes up the dependency chain: if a Kennel
wants to create a Dog
object, it will also need to request a SoundManager
from its caller.
You can imagine that it gets tedious to pass all these constructor arguments, especially as your codebase grows. That's where the frameworks are useful.
CodePudding user response:
As you said It quite a common problem in OOP, and the Idea you have come up with is a good idea but the implementation is poor, for alot of reasons,
What you are trying to do here is the concept of a Context And Dependency Injection
.
You want a Class that is available to supply instances for you on the go:
SrorageManager storageManager = ComponentsHub.getStorageManager();
But you need to think about some important aspects, from the top of my head:
- Mutability
- Thread-Safty
And ofcourse Separation Of Concern. Some of those Classes might be subject to multiple threads, some might be Singletons and some might be just Utility classes.
So I propose first; Interfaces
Separation: Define your contracts.
Security Context
public interface SecurityContext {
public SecurityManager getSecurityManager();
public Authemticator getAuthenticator();
}
WebContext
public interface WebContext {
public Uploader getUploader();
public RequestForwarder getRequestForwarder();
public Server getServer();
.
.
.
}
And maybe a Data or Persistence Context.
Thread Safe
If you are considering being able to support multi-threading you need to take into consideration a way for control and synchronisation, either using java API Locks or using the syncronized keyword, or maybe have some thread local work isolated, depends on your case.
Mutability
Who can change the state of the contexts? Are contexts created as one-per-application or is it one-per-thread (request) like a database transaction for example.
Do you want to have everything stating from app and never to be changed at runtime for example, maybe a DB credentials changed, maybe you want to implement another Authenticator, those classes should be dynamic, and not static, and a context have the flexibility to give you different implementations.
To summerize I what I think you should be able to do is:
- Create the factories/suppliers/instances of the classes in the fields outside the context let's call those dependencies
- initialize the context, Using builders and inject the required dependencies
new PersistanceContextBuilder.withStorageManager(new FileSystemStorageManager).build();
- Figure out a way to provide those context to the rest of the application, preferably Create Services or Controllers that are also separated and depend on different contexts.