DVK
DVK

Reputation: 129413

How should I implement a singleton class if I need to use method overloading in its construction?

I'm trying to implement something to this effect:

  1. Base class A has a method getFileName()
  2. Two derived classes B and C have overridden implementations of that method, returning file names specific to B and C.
  3. Class A needs to use the services of a singleton class S
    • The reason I want it to be a singleton is because (a) I want a guarantee that it will only be constructed once and (b) I want eager initialization of that class, which happens at app startup, and not at first use.
  4. Class S needs to do its work work based on the file name (E.g., read in the contents of that file) - which depends on which of A's subclasses is used.

This seems to present an unavoidable conundrum, because:

How can I implement this design? (I'm open to changing the design if a better pattern is available)

Upvotes: 3

Views: 966

Answers (3)

user177800
user177800

Reputation:

... 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.

Upvotes: 2

Edwin Buck
Edwin Buck

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

  1. Having your objects passed to the non-object oriented singleton "utility routine"

  2. 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

rogierschouten
rogierschouten

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

Related Questions