Software Engineering
java singleton factory
Updated Sun, 11 Sep 2022 12:39:17 GMT

Should I design a factory that returns a singleton?


I design a common API for selected printers of different brands in Java. Each printer uses a different underlying SDK with different functions, but any hardware my code runs on will have only one printer connected, this is a known guaranteed hardware limitation. Despite dealing with different SDKs, I also have common logic for any printer, such as a single thread executor. So, my goal is to ensure that only one instance of any Printer class can exist as well as having common logic in a common parent class.

My solution is to have public abstract class (instead of an interface) for the common logic (Printer), while creating one class for each printer type to override abstract functions that require specific implementations (such as accessing the underlying SDK of the printer) (PrinterA, PrinterB, PrinterC).

Now I have built a factory class PrinterFactory with a static async (concurrent) getPrinter() method that will check the hardware to see which of the printers is connected and then cache its instance within the factory to prevent more instances to be created.

I've come to the conclusion that I have build a "singleton factory" that produces and returns a polymorphic singleton.

Here is a rough example how it looks like:

public final class PrinterFactory {
    private static Printer instance;
    public static CompletableFuture<Printer> getInstance() {
        return CompletableFuture.supplyAsync(() -> {
            if (PrinterFactory.instance == null) {
                throw new Error("Not implemented.");
                // TODO: Determine the printer and create its instance and store it in PrinterFactory.instance
                // i.e: PrinterFactory.instance = new PrinterA(); // inherits Printer
            }
            return PrinterFactory.instance;
        });
    }
}

My question is, is this pattern a good move? Also, is the class term "PrinterFactory" even applicable since it only constructs one instance in its entire existence within the process lifetime?




Solution

It's not great...

My question is, is this pattern a good move? Also, is the class term "PrinterFactory" even applicable since it only constructs one instance in its entire existence within the process lifetime?

The problem I see with this implementation is that there are two cooks in charge of making the same broth.

A factory's job is to provide a public point to generate Foo from. You use it when consumers cannot feasibly create their own Foo "in the field" (as it were), and the factory provides that ability for them.

A singleton is designed to reduce instantiation to a single instance; but it almost inherently also comes with a way to access this single instance. In effect, it also acts as a public point to "generate" Foo from. By that I mean that consumer's don't actively care whether they're getting a newly created Foo or a reused pre-existing Foo.

There is no need to develop this responsibility twice. The two cooks are battling over the same broth.


... but it's not impossible either.

That being said, it is possible that this is a meeting of what was once two separate features.

For example, let's say this is about data access. You used to use a library that required generating new connection objects, and therefore you wrote a factory that generated these objects. Your entire codebase now uses that factory.

Today, you are changing your DB provider, and the new provider requires you to reuse the same connection object (for the purposes of this example, it is done via a singleton).

You don't want to rewrite your entire codebase to no longer use the factory and use the singleton instead, so you come up with a different solution: the factory simply returns the singletone. This way, you don't need to change your old code, and this all works.


Conclusion

The conclusion here is that having a factory return a singleton is somewhat superfluous and is better avoided when you're building something from scratch, simply as a matter of not doing the same thing twice or muddying the line between the responsibilities.

However, if you're trying to not have to rewrite legacy code, it can make sense to end up making a factory return a singleton just to avoid having to remove prior dependencies to the factory.

I would try to avoid it if you can, but I wouldn't go as far as to say that it should never exist either.





Comments (5)

  • +0 – "There is no need to develop this responsibility twice." - I'm not going to. My idea is to have the constructor of PrinterA, PrinterB and PrinterC package-private and no singleton logic there, so that I end up with a hybrid factory without redundancy that takes care of that all by itself. If I get you right though, you say I should separate it, so I should implement the singleton in every child-class (or even parent class) and let the factory only return PrinterX.getInstance(); for the sake of not mixing up both concepts into one "superfluous" class. Am I correct in my understanding? — Jul 25, 2022 at 08:51  
  • +1 – @MartinBraun: The general point is more that it's a weird approach that I wouldn't take without an explicit reason to do so. There can be reasons to do so, but I would (in my role as tech lead) require a justification why it's a good idea to do it. In absence of such a justification, I would avoid it. — Jul 25, 2022 at 08:59  
  • +0 – My explicit reason would be: The printer recognition costs time, so my sub-class should be cached within the factory. If I move the singleton into the sub-classes I would need to create an additional enum field in the factory to remember the result of my printer recognition logic. I would also end up with redundant code. I got the opportunity to eliminate redundancy between the sub-classes by moving the singleton logic into my factory. I will definitely think this through another time, so please don't hesitate to give some feedback for my explicit reason to do it. I value your input. — Jul 25, 2022 at 16:37  
  • +0 – @MartinBraun: There is a difference between having a factory that caches its produced results, and a class that is made to be a singleton. It sounds like you're in need of the former. The latter is overkill and tends to have significant side effects in the long run. — Jul 26, 2022 at 11:15  
  • +0 – Since the parent Printer class has a dedicated executor for the jobs, a singleton is still great to prevent print collisions and memory redundancy, but your answer triggered re-thinking on my end. I decided to split-off singleton and factory logic to avoid a superfluously concept at least. Hopefully others will benefit from my question and all the answers incl. yours in the future as well. — Jul 26, 2022 at 12:55