Reputation: 350
I have a program where I need to read and write data to a common (central?) object from most other obejcts. In a way it acts like a database or perhaps databus if you like. I cannot write it to a file. Currently I do it like this:
class A {
private Common common;
private B b;
public A() {
common = new Common();
b = new B(common);
}
}
class B {
private Common common;
private C c;
public B(Common common) {
this.common = common;
c = new C(common);
}
}
class C {
private Common common;
private D d;
public C(Common common) {
this.common = common;
d = new D(common);
}
}
...etc.
So I am passing the object "down the line" but it feels wrong doing it like this (given the amount of objects I am getting to). Is there a better way of doing this?
Edit: Class A can be instantiated multiple times, so Common is only common for all the objects in each individual A.
Upvotes: 1
Views: 503
Reputation: 8204
This is the kind of problem that "dependency injection" frameworks like Guice are intended to solve. Many Java applications have a set of classes that model "the application," as opposed to the data that the application works on. The app might have a Server
, and a WidgetService
, and a Database
, a DatabaseWidgetReader
, and so on. These classes all collaborate with each other to implement the application logic, and each one is usually accepts its collaborators as constructor arguments.
But once you have more than a few such classes, and you have a big initialize
method that constructs everything, and every test has to have its own version of the big initialize
method, you start thinking that there must be a better way.
With a framework like Guice you just annotate your constructor parameters with @Inject
, provide some bootstrap configuration, and let the framework do the rest. To make your example work with Guice, you would:
A
constructor should take both Common
and B
.Common
to a specific instance that you create. (I'm assuming here that Common
needs some information that it can't get from Guice, maybe the address of a database server.)Usually the class you specify in the last step will be something like Server
or Application
or something that is the entry point for the whole app.
Let's say you ask for A
in the last step. Guice says, okay, I need a Common
, which I have (provided in the third step above), and an instance of B
. In order to get B
, I need a Common
and a C
, etc. Obviously this chain has to end somewhere. Guice will trace out all the dependencies and construct everything, then return you the instance of A
.
If you have some objects that you don't want to be singletons (e.g., you want to instantiate a new CPU
every time you need one but want all CPU
instances to share the same Memory
) you can register a "provider" that Guice will use to create new instances.
There are other frameworks that work more or less the same way; I'm just most familiar with Guice.
Upvotes: 0
Reputation: 15706
When sharing such objects, and not passing them as argument as methods, you need to have something that provides you the instance. That something can
The base Design Pattern for such an object is the Singleton pattern, where the (single) instance is created and provided by the class of that instance.
The usage would look like this:
Common common = Common.instance();
When more advanced pattern for such objects is the Registry pattern (see Patterns of Enterprise Application Architecture by Martin Fowler), where a dedicated singleton (the registry) creates and provides these instances.
The usage would look like this:
Common common = Registry.instance().get(Common.class);
Frameworks such as JavaEE (CDI) and Spring leverage the registry pattern, and use a Bean Registry to manage such instances, and supply them over Dependency Injection.
Upvotes: 0
Reputation: 70909
The real answer is "don't".
You can use the Singleton
pattern; but, by doing so you are avoiding "object oriented programming" which is the method that Java uses to make the programs maintainable and repairable.
Once you have a single, central object, you can't modify your code easily in many ways without investigating how the central object is impacted, and then investigating how all the other objects that also use the central object are impacted through the central object.
Instead create an object for each scenario. This allows you to process two scenarios at a time, which will make life easier when you start to make your programs multi-threaded, something you'll want to do sooner or later, because otherwise you'll only use 1 core in that 8 core processor.
Your language assumes a central object, so I can't provide an example in your scenario. I'll modify Commmon
to Context
where Context
means "the scenario I'm dealing with now".
Note that with such a change, you probably shouldn't own the context, you should pass it along.
class A {
private B b;
public A() {
b = new B();
}
public handle(Context context) {
context.add(...whatever...);
b.handle(context);
}
}
class B {
private C c;
public B() {
c = new C();
}
public handle(Context context) {
context.remove(...whatever...);
context.add(...something else...);
c.handle(context);
}
}
class C {
private D d;
public C() {
d = new D();
}
public handle(Context context) {
context.do(...whatever...);
d.handle(context);
}
}
The way to call this is now
Context c1 = new Context();
Context c2 = new Context();
A a = new A();
a.handle(c1);
a.handle(c2);
And as you can see, there's no longer a need for a single "central" class.
Fitting the problem into the language, or selecting the language for the problem is a learned skill. Please avoid Singleton, as it is a hard learned anti-pattern, one that hurts software maintainability.
If you wrote all of your code with Singletons, the code could easily be ported into a non-object oriented language (like C, FORTRAN, etc) because Singletons are syntatic sugar for "global scope, with slightly better name spaces". The reason that C++, Java, etc became popular was due to the easier code maintenance of structuring code into Objects.
Upvotes: 4