Reputation: 129413
I'm trying to implement something to this effect:
getFileName()
This seems to present an unavoidable conundrum, because:
most implementations of Singleton are static based (pure static class; or ENUM with a static parameter passing)
static classes/methods/blocks cannot call non-static methods...
... and making getFileName()
static will make sure that it cannot use inheritance overrides!
How can I implement this design? (I'm open to changing the design if a better pattern is available)
Upvotes: 3
Views: 966
Reputation:
That means the Singleton
is not really your problem, it is the acquisition of the correct class based on the type asking!
Your design is too tightly coupled the way you are trying to do it. You need to completely decouple the Service
from the Consumers
of the service, Singleton
is not important in this exercise.
This is exactly the type of problem that Guice
was created to solve by being able to provide what classes get injected based on another classes type in a binding. That said ...
Most people do not realize that Java has always supported DI
via the Constructor
. Guice
makes this less hard coded, but it is still a dependency that is injected to an instance.
Guice
would make this trivial by injecting the correct service based on the class type. But it can be done without any DI
framework/library. If using Guice
is considered to heavy handed for your case then it can still be done easily.
public class Solution
{
static class Singleton
{
public static final Singleton INSTANCE;
static { INSTANCE = new Singleton(); }
private Singleton() { /* this is important */ }
public void doWhatever(@Nonnull final B b) { /* whatever */ }
public void doWhatever(@Nonnull final C c) { /* whatever */ }
}
static abstract class A
{
private final Singleton s;
public A(final Singleton s) { this.s = s; }
public abstract String getFilename();
}
static class B extends A
{
public B(final Singleton s) { super(s); }
@Override
public String getFilename() { /* code goes here */ }
}
static class C extends A
{
public C(final Singleton s) { super(s); }
@Override
public String getFilename() { /* code goes here */ }
}
}
The Singleton
pattern should by hidden behind a Factory
pattern. Your consumers of what needs to have 1 and only 1 should not care if there is 1 and only 1. They should only care that that object conforms to some contract of some interface.
My implementation is a naive Factory
to create in static block. Most are create on first use which is not any better.
Using Enum
to create Singleton
objects is a misuse of the semantics of Enum
and an anti-pattern and impossible to properly unit test.
Same with the all static utility class approach, impossible to unit test or replace with a different implementation. A combination of the two is a complete abomination that is impossible to unit test and a complete nightmare to maintain!
A
the Singleton works on is easy:That is what overloading is for as shown in the code above.
Anything else is not doing it right. instanceof
fail, reflection
bigger fail.
Selecting logic based on Type
can be done with overloading methods, or generics or with the appropriate design pattern.
Strategy
Pattern would account for that easily and make N
number of subclasses manageable and extensible at runtime.
Upvotes: 2
Reputation: 70909
I think you need to decide if S uses A or if A uses S.
If S uses A, then A could be a base class or interface, and S would have a method that accepts instances of A, which are overridden with the correct implementation of getfileName()
.
If A uses S, then A should be abstract with respect to getFileName()
forcing an implementation to be constructed, and it should internally call it's yet-to-be-defined getFileName()
passing that as an argument to S.
Singletons are the glue between Object-Oriented solutions and non-Object-Oriented solutions, so you avoid the conundrum by
Having your objects passed to the non-object oriented singleton "utility routine"
Having the resolved parameters passed to the non-object oriented singleton "utility routine"
Example code for the first technique
// this could be abstract class too, as long as getName() is abstract
public interface Nameable
public String getName();
}
public enum Utility {
INSTANCE;
public static deleteByName(Nameable nameable) {
createBackup(nameable.getName());
updateIntentLog(nameable.getName());
removeFile(nameable.getName());
updateAuditLog(nameable.getName());
}
}
or
public abstract class Nameable {
public abstract String getName();
public void delete() {
Utility.INSTANCE.deleteFile(getName());
}
}
public enum Utility {
INSTANCE;
public void deleteFile(String name) {
...
}
}
Upvotes: 0
Reputation: 153
You can make singleton classes that you initialize manually, i.e. have a static instance variable but also a static initialize() method. The initialize throws if you try to initialize twice. This allows you to choose at run-time which subclass to use and also it makes the initialization order clear.
Upvotes: -1