... needs to use the services of a singleton ... which depends on which of A's subclasses is used:
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.
What you need is some form of dependency injection.
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.
Below is one way to do it without a framework/library:
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 anti-patterns you mention are just that:
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!
How you determine which subclass of 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.