Limit Instantiation with Singleton

Prev don't be afraid of buying books Next

Limit Instantiation with Singleton

Your code creates multiple instances of an object, and that uses too much memory or slows system performance.



Replace the multiple instances with a Singleton.



Motivation

If you want to be a good software designer, don't optimize code prematurely. Prematurely optimized code is harder to refactor than code that hasn't been optimized. In general, you'll discover more alternatives for improving your code before it has been optimized than after.

If you use the Singleton [DP] pattern out of habit, because it "makes your code more efficient," you're prematurely optimizing. You suffer from Singletonitis and had better follow the advice in Inline Singleton (114). On the other hand, sometimes it's a good decision to refactor to a Singleton, as in the following scenario.

  • Users of your system are complaining about system performance.

  • Your profiler tells you that you can improve performance by not instantiating certain objects over and over again.

  • The objects you want to share have no state or contain state that is sharable.

A colleague and I profiled a system that handles security permissions. The system uses the State [DP] pattern (see Replace State-Altering Conditionals with State, 166). Every state transition leads to the instantiation of a new State object. We profiled the system to check memory usage and performance. While the instantiation of State objects wasn't the biggest bottleneck in the system, it did contribute to slow performance under a heavy load.

Based on this research, we determined that it made sense to refactor to a Singleton in order to limit the instantiation of stateless State objects. And that's the general idea behind this refactoring: wait for a good reason to limit instantiation and when you find one, then refactor to a Singleton. Of course, we profiled after implementing the Singleton, and memory usage was much improved.

For other reasons to refactor to a Singleton that don't involve improving performance, see the wise words provided by Kent Beck, Ward Cunningham, and Martin Fowler earlier in this catalog at Inline Singleton (114).

Benefits and Liabilities

+

Improves performance.

Is easily accessible from anywhere. In many cases, this may indicate a design flaw (see Inline Singleton, 114).

Is not useful when an object has state that can't be shared.







Mechanics

Before you perform this refactoring, make sure the object you want to turn into a Singleton has no state or has state that is sharable. Because most classes that end up becoming Singletons have one constructor, these mechanics assume you have one constructor on your class.

  1. Identify a multiple instance class, a class that gets instantiated more than once by one or more clients. Apply the mechanics from Replace Constructors with Creation Methods (57) even though your class has only one constructor. The return type for your new Creation Method should be the multiple instance class.

    • Compile and test.

  2. Declare a singleton field, a private static field of type multiple instance class in the multiple instance class and, if possible, initialize it to an instance of the multiple instance class.

    It may not be possible to initialize this field because to do so, you need arguments passed by a client at runtime. In that case, simply define the field and don't initialize it.

    • Compile.

  3. Make your Creation Method return the value in the singleton field. If it must be lazily instantiated, do that lazy instantiation in the Creation Method, based on whatever parameters are passed in.

    • Compile and test.

Example

This example is based on the security code example found in the refactoring Replace State-Altering Conditionals with State (166). If you study the code produced after applying that refactoring, you'll find that each State instance is a Singleton. However, these Singleton State instances weren't created for performance reasons; they resulted from performing the refactoring Replace Type Code with Class (286).

When I initially refactored to the State pattern on the security code project, I did not apply Replace Type Code with Class (286). I wasn't yet aware of how much that refactoring simplifies the later steps in refactoring to the State pattern. My earlier approach to the State refactoring involved instantiating Permission subclasses each time they were needed, paying no regard to the Singleton pattern.

On that project, my colleague and I profiled our code and found several places where it could be optimized. One of those places involved the frequent instantiation of the state classes. So, as part of an overall effort to improve performance, the code to repeatedly instantiate the Permission subclasses was refactored to use the Singleton pattern. I describe the steps here.

1. There are six State classes, each of which is a multiple instance class because clients instantiate them multiple times. In this example, I'll work with the PermissionRequested class, which looks like this:

 public class PermissionRequested extends Permission {    public static final String NAME= "REQUESTED";    public String name() {       return NAME;    }    public void claimedBy(SystemAdmin admin, SystemPermission permission) {       permission.willBeHandledBy(admin);       permission.setState(new PermissionClaimed());    } } 

PermissionRequested doesn't define a constructor because it uses Java's default constructor. Because the first step in the mechanics is to convert its constructor(s) to Creation Methods, I define a Creation Method like so:

 public class PermissionRequested extends Permission...     public static Permission state() {        return new PermissionRequested();     } 

You'll notice that I use Permission as the return type for this Creation Method. I do that because I want all client code to interact with State subclasses via the interface of their superclass. I also update all callers of the constructor to now call the Creation Method:

 public class SystemPermission...    private Permission state;    public SystemPermission(SystemUser requestor, SystemProfile profile) {       this.requestor = requestor;       this.profile = profile;         state = new PermissionRequested();        state = PermissionRequested.state();       ...    } 

I compile and test to make sure that this trivial change didn't break anything.

2. Now I create the singleton field, a private static field of type Permission in PermisionRequested, and initialize it to an instance of PermissionRequested:

 public class PermissionRequested extends Permission...     private static Permission state = new PermissionRequested(); 

I compile to confirm that my syntax is correct.

3. Finally, I change the Creation Method, state(), to return the value in the state field:

 public class PermissionRequested extends Permission...    public static Permission state() {        return state; } 

I compile and test once again and everything works. I now repeat these steps for the remaining State classes until all of them are Singletons. At that point, I run the profiler to check how memory usage and performance have been affected. Hopefully, things have improved. If not, I may decide to undo these steps, as I'd always rather work with regular objects than Singletons.

Amazon


Refactoring to Patterns (The Addison-Wesley Signature Series)
Refactoring to Patterns
ISBN: 0321213351
EAN: 2147483647
Year: 2003
Pages: 103

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net