2

I've seen a lot of posts recently on why Singletons should be avoided. However, I can't see any of those problems with the following solution to a common problem: static inheritance.

For example, I was recently working on a library that scrapes the HTML source of a website and parses its contents into objects. The problem here is, the site has different types of content- articles, videos, etc. In my initial code, I had several different static 'supplier' classes- i.e. ArticleSupplier- that I quickly found shared a lot of boilerplate code (i.e. argument checks).

I could have made the classes instance-based, but it seems pointless: new ArticleSupplier() has no state; all its methods depend on the arguments passed to them. Instantiating another one is just a waste of memory.

This has brought me to thinking about using the Singleton pattern: a base class could perform boilerplate code checks and then call the derived class' implementation; the derived class could then control access with a singleton. Here's a simple example in C#:

abstract class Supplier
{
    IEnumerable<IModel> RecentModels(int amount)
    {
        return ModelsBefore(DateTime.Now, amount);
    }
    IEnumerable<IModel> ModelsBefore(DateTime date, int amount)
    {
        CheckDate(date);
        CheckAmount(amount);
        return InternalBefore(date, amount);
    }
    abstract IEnumerable<IModel> InternalBefore(DateTime date, int amount);
}

class ArticleSupplier
{
    private static readonly ArticleSupplier _Instance = new ArticleSupplier();
    private ArticleSupplier() { }
    public static ArticleSupplier Instance { get { return _Instance; } }
    override IEnumerable<IModel> InternalBefore(DateTime date, int amount)
    {
        // implementation
    }
}

Now, you can focus on the implementation within ArticleSupplier without writing the same boilerplate code, and at the same time control instantiation of the object. I'd like to emphasize again that this is completely thread-safe because the singleton has no state, which is why the method was static in the first place.

Is this an appropriate use of the Singleton pattern, or are there still some pitfalls that I'm missing?

  • Are you calling ArticleSupplier _Instance a Singleton because it's static? Anyway, this is just ordinary composition, not inheritance. – Robert Harvey May 28 '15 at 1:15
  • This reads like you are trying to create a factory, not a singleton, because you are selecting an implementation of an algorithm based on inputs. Do I understand your problem correctly? – user22815 May 28 '15 at 1:19
  • @RobertHarvey Oops, the Instance property should be static. Fixed. – James Ko May 28 '15 at 1:22
  • @Snowman Yes, that is what I'm trying to do. Last time I tried that, though, I ended up with lots of switch (type) code, like in here. – James Ko May 28 '15 at 1:26
  • Yep, that's what Factories inevitably look like. – Robert Harvey May 28 '15 at 1:30
5

Is this an appropriate use of the Singleton pattern, or are there still some pitfalls that I'm missing?

No, this has almost all of the pitfalls of singletons and even that assumes that you actually use Supplier almost everywhere and have other implementations. I'm not going to go into the various pitfalls of singletons as they have been covered well elsewhere.

Instantiating another one is just a waste of memory.

4 bytes. You are limiting your design for 4 bytes? Yes, it currently has no state. Yes it is currently threadsafe. It seems entirely plausible that a future version will need to parameterize the location of your articles, or perhaps some info about how the articles are sharded by creation time, or by any number of things. But you want to hamstring yourself because of 4 bytes?

The singleton is an antipattern. You are not going to change that.

  • 1
    Please note that on a 64 bit architecture, that would be 8 bytes. Also, with heap memory, there might be a few more bytes set aside for metadata. Still, far less memory than this comment takes up. – user22815 May 29 '15 at 9:54
  • @Snowman Not necessarily. Since heap objects are aligned on 8-byte boundaries you can chop off the lowest 3 bits without losing anything, and store the offset from the start of the Java heap rather than the absolute address. That lets you address 32 GB of memory with 32 bits, at the cost of some extra arithmetic. – Doval May 30 '15 at 0:42
2

What you are looking for is not a singleton, but a factory. The reason is you have a single object that figures out which logic to execute, then executes it. As you say in your comment, it is functionally similar to a switch statement: figure out which branch to execute, then execute it.

That works fine if your logic is simple, but once it grows in complexity, the switch or cascaded if grows cumbersome. Furthermore, you want the code to load implementations dynamically, not via compile-time selection. It is better to bury this logic in an object instead of one long method. I recommend the following components:

  • Each "method" or algorithm, i.e. each "supplier" in your example, has a pair of classes:

    • An actual supplier that does whatever your problem needs it to do.

    • A service provider that determines if this particular supplier can handle the input, and is able to create the supplier.

  • One class that represents input data for easy passing between methods (no overloading necessary).

  • One factory class that accepts input and returns an object capable of processing it.

Example in pseudocode:

class Input {
  public DateRange range;
  public int quantity;
}

interface ServiceProvider {
  boolean canHandle(Input i);
  object newHandler(object o);
}

class Factory {
  List<ServiceProvider> sps = new ArrayList<>();

  public static void register(ServiceProvider sp) {
    sps.add(sp);
  }

  public static Supplier getSupplier(Input i) {
    for (ServiceProvider sp in sps) {
      if (sp.canHandle(i)) {
        return sp.newHandler(i);
      }
    }
    return null;
  }
}

Notes:

  • This replaces switch with dynamic dispatch via traversing the list and finding an instance that can handle the input.

  • This is a simple example to give you the idea. Depending on the specific problem at hand, it may be better to use a hash table or other data structure.

  • Service providers need to register themselves. The specific mechanism to do this is dependent on the language (your question is not tagged with a specific language).

  • Thank you for your time, but I seem to have misconveyed something. It's not that I don't like switch in particular, it's that I don't want to have to go back to the original file and add something (in this case, new SupplierC()) every time I add a class, which does not happen in my solution. – James Ko May 28 '15 at 1:53
  • @JamesKo please read the edits I made to this answer. – user22815 May 29 '15 at 11:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.