Building A Better Singleton
Posted: April 25, 2020

Building A Better Singleton

Table of Contents:

  • Getting Some Singleton Inspiration
  • Testing The Initial Result
  • Singleton Pattern Conclusion

In the last post on Picklist Validation, I touched briefly on my disappointment in being unable to bring to fruition a better pattern for creating singletons — the perfect way to access constant values like those in a picklist:

I hate writing singleton property accessors, and have spent more hours than I’d care to admit trying to “solve” that problem through a variety of abstract class hacks … none of which has borne any fruit. That’s the nature of progression, though — failure is a part of the process.

In the ensuing weeks, I spent a considerable portion of time simply spent bulleting out thoughts about the “problem” that I had first introduced in the Idiomatic Apex post:

  • the traditional get/set method for creating singletons takes nine lines of code. NINE. Nine lines! That’s way too much.
  • because singletons need to be accessed statically (so that you don’t have to initialize new objects each time you are calling them), there is an inherent disconnect between the ideal object-oriented solution (which would require the usage of the this keyword, strictly verboten in a static context), and our options on the table.
  • we can set static variables in our constructors though … might that be enough?

Getting Some Singleton Inspiration

Last night, while winding down, the situation finally resolved itself in a burst of inspiration. Messily implemented first in my notebook (my handwritten braces get progressively worse), I finally figured out how to resolve the static nature of singletons with an object-oriented approach:

public abstract class Singleton {
  private static final Map<Type, Singleton> typeToSingleton
    = new Map<Type, Singleton>();

  public static Singleton getSingleton(Type type) {
    if(typeToSingleton.containsKey(type) == false) {
      typeToSingleton.put(type, (Singleton)type.newInstance());
    }
    return typeToSingleton.get(type);
  }
}

Going back to the Picklist class from the previous post, now the base class just needed to extend Singleton so that subclasses could make use of the type:

public abstract class Picklist extends Singleton {
  // ...etc
}

public class AccountIndustries extends Picklist {
  public AccountIndustries() {
    super(Account.Industry);
  }

  // the newer invocation
  // note that because there isn't a "set"
  // method, if I wasn't going for mobile
  // friendly code, this could be reduced to a single line
  public static AccountIndustries Current {
    get {
      return (AccountIndustries)Singleton.getSingleton(AccountIndustries.class);
    }
  }

  // the more idiomatic singleton, for testing purposes
  public static AccountIndustries Instance {
    get {
      if(Instance == null) {
        Instance = new AccountIndustries();
      }
      return Instance;
    }
    private set;
  }

  public String AGRICULTURE { get { return this.getValue('Agriculture'); }}
  public String APPAREL { get { return this.getValue('Apparel'); }}
}

Of course, it’s not enough to simply have code that compiles — is it performant? Let’s do some simple iteration to stress test this new Singleton pattern:

@IsTest
private class SingletonStressTests {
  @IsTest
  static void it_should_establish_a_baseline_iteration_time() {
    runTest(null);
  }

  @IsTest
  static void it_should_use_idiomatic_singleton() {
    runTest(new TestIdiomaticSingleton());
  }

  @IsTest
  static void it_should_use_new_singleton() {
    runTest(new TestNewSingleton());
  }

  static void runTest(TestFunction function) {
    for(Integer index = 0; index < 10000; index++) {
      if(function != null) {
        function.call();
      }
    }
  }

  private abstract class TestFunction {
    public abstract void call();
  }

  private class TestIdiomaticSingleton extends TestFunction {
    public override void call() {
      System.debug(AccountIndustries.Instance.AGRICULTURE);
    }
  }

  private class TestNewSingleton extends TestFunction {
    public override void call() {
      System.debug(AccountIndustries.Current.AGRICULTURE);
    }
  }
}

Testing The Initial Result

Woof. Initial results were not promising:

TEST NAME OUTCOME RUNTIME (MS)
SingletonStressTests.itShouldEstablishABaselineIterationTime Pass 23
SingletonStressTests.itShouldUseIdiomaticSingleton Pass 240
SingletonStressTests.itShouldUseNewSingleton Pass 1160

At first, I wondered if perhaps the dynamic Type.newInstance, or perhaps even the usage of the internal Map within the Singleton class was responsible for the slowdown. I would expect that there would be some slowdown, some overhead, in the usage of this more complicated setup … however, I did not expect that the new method would be six times slower. Of course, in actual usage, such slowdown might not matter for your application … but that’s not the Joys Of Apex way. While I was ecstatic at the notion of saving 8 lines of code through the use of this new singleton one-liner, I wasn’t about to recommend that to my clients if it meant taking such a big performance hit.

I tried eliminating the map. I tried passing an actual instance of the class to the getSingleton function (in this case, using new AccountIndustries) instead of dynamically spinning an instance up. Nothing. No changes reduced the runtime appreciably.

Then it hit me — the property Current itself was not being cached. Just for kicks, let’s switch to the more idiomatic method for instantiating singletons to see if that made up any ground in terms of performance:

public static AccountIndustries Current {
  get {
    if(Current == null) {
      Current = (AccountIndustries)Singleton.getSingleton(AccountIndustries.class);
    }
    return Current;
  }
  private set;
}

The results were fascinating:

TEST NAME OUTCOME RUNTIME (MS)
SingletonStressTests.itShouldEstablishABaselineIterationTime Pass 27
SingletonStressTests.itShouldUseIdiomaticSingleton Pass 109
SingletonStressTests.itShouldUseNewSingleton Pass 85

OK, so the pattern was not itself responsible for the performance slowdown. That was great news. It wasn’t great news that it still took 9 lines of code to retrieve a singleton instance. Apex does feature static constructors, but those are no good; was there any way to ensure that the property was only initialized once without all the boilerplate?

Perhaps you see where this is headed now. There is, of course, one last trick up our sleeves — the final keyword. Traditionally used to ensure an object’s dependencies are set only in the constructor, final is also compatible with static variables and ensures that they are only ever initialized once.

That makes the AccountIndustries object look pretty svelte indeed:

public class AccountIndustries extends Picklist {
  private AccountIndustries() {
    super(Account.Industry);
  }

  public static final AccountIndustries Current = (AccountIndustries) Singleton.getSingleton(AccountIndustries.class);

  // only keeping this property now to re-run the tests
  public static final AccountIndustries Instance = new AccountIndustries();

  public String AGRICULTURE { get { return this.getValue('Agriculture'); }}
  public String APPAREL { get { return this.getValue('Apparel'); }}
  // etc, adding constants as is necessary to
  // represent your picklists values in code
  // with minimal usage of "magic" strings
  // and the added benefit of intellisense
}

Running the tests again:

TEST NAME OUTCOME RUNTIME (MS)
SingletonStressTests.itShouldEstablishABaselineIterationTime Pass 23
SingletonStressTests.itShouldUseIdiomaticSingleton Pass 98
SingletonStressTests.itShouldUseNewSingleton Pass 90

Easy peasy. I ran the tests dozens of times — for whatever reason, the newer method was always a few milliseconds faster than simply caching the instance. One can only surmise that there exists some fairly interesting tail-end optimizations in how the compiler assembles the Type.newInstance code which gives it a slight edge over the use of the new keyword.

Singleton Pattern Conclusion

Lessons learned in building a better singleton:

  • you can have your object-oriented cake and eat it too
  • usage of the final keyword should be considered the idiomatic method for instantiating singletons

One thing that should be noted with the usage of Type.newInstance() — it requires the usage of a public zero-argument constructor. That shouldn’t come as a surprise for those of you following along from the Factory post. It does however, fly in the face of the more classic singleton pattern (which makes the constructor private in order to force invocation through the public static variable). That’s definitely something to consider when making your object design choices. The limitations of the Type class in Apex have frequently proven less-than-ideal in testing, as well, since it requires elevating test classes to public that shouldn’t have to have elevated visibility. As always, food for thought.

For more examples of when the Singleton pattern can come to your rescue, check out how invocable and static-based code can benefit from using this pattern, or perhaps how it can be combined with the flyweight pattern in Replace Conditional With Polymorphism!

I hope that this post proved educational for you. The usage of singletons is fairly common, and knowing that you have some tricks up your sleeve when you need to implement one is always a good thing. Till next time!

In the past three years, hundreds of thousands of you have come to read & enjoy the Joys Of Apex. Over that time period, I've remained staunchly opposed to advertising on the site, but I've made a Patreon account in the event that you'd like to show your support there. Know that the content here will always remain free. Thanks again for reading — see you next time!