[Programming] [Photos] [About Me]


[Back to Approaches and Techniques]

Objects: Create, Store & Get

This article describes approaches and techniques I applied in my open-source project WholeTrack. It describes a strategy for object creation and proposes a solution that allows to eliminate class mutual dependencies, to make objects globally accesible and to reduce risks concerned with global accessibility.

Suppose the situation: class A calls the method M() of class B.


Figure 1. Class A calls the method M() of class B.

This is rather simple, isn’t it? The question is: how should instance of A obtain the instance of B? Let’s examine several options.

Three Ways To Get a Class Instance

Option #1. A creates instance of B itself


Figure 2. Class A creates instance of class B itself.
  • Problem: In many cases the class A do not have enough data to create the instance of B
  • Problem: It is not always simple to create the instance of B; it can be necessary to write the code that reads parameters from the configuration file, determines the specific class, instance of which should be created, and performs the initialization of that instance. Probably the business logic code will get mixed up with the initialization code
  • Problem: The instance of B can be consumed by A by the interface (abstract class) reference. It can be not possible to determine the specific class at the moment that you need to use one

Figure 3. Class A comsumes objects using abstract class reference.
  • Problem: It is not always the option to re-create the instance of B every time the instance of A is re-created
  • Problem: If the creation of instance of class B is resource-intensive operation, the class A should save it somewhere for future use
  • Problem: If some other class needs the instance of B, it should obtain it somehow from the instance of A

Option #2. A gets the instance of B from C, by the call stack


Figure 4. Class A instance gets the instance of B from C, by the call stack.
  • Problem: The dependency appears between C and A. When A changes, C should be changed as well

Figure 5. The dependency appears between C and A.
  • Problem: In many cases C is not the owner of the instance of B. It may be necessary to pass the instance of B to the C itself (from the D). It can happen that C takes the instance of B just to pass it further to A

Figure 6. Chain of calls for passing the instance of B.
  • Problem: In many cases high-level classes do not have enough credentials to store low-level class instances. Utilizing the mentioned approach can result in storing all objects in the main application form class

Option #3. A gets the instance of B from the static class field or from the singleton field


Figure 7. Class A gets the instance of B from the static class field or from the singleton field.
  • Problem: Objects can be created in the different parts of the application, at the different moments and have a different scope. Using of the singleton doesn’t specify such terms by itself
  • Problem: If some object re-creates an instance of B that was stored in the singleton, other objects may keep the outdated reference of B
  • Problem: Sometimes you should initialize object before using it. You need to choose the right place for the initialization code. In many cases the initialization code is put in the beginning of the program, but at this moment not all the details needed may be known
  • Problem: By the reason above the initialization code can be spread by the whole application code
  • Problem: It is often needed to create objects in order because of their dependencies. The initialization code can become too complicated and fragile
  • Problem: In many cases you will need to re-initialize some objects. At the same time the dependencies and the initialization order should be taken into account

Solution

Let’s define 2 kinds of objects:

  • Short-living objects. Such objects can be replaced with other objects at any moment and from any place. It is acceptable if they don’t exist at all. Each time you use the object of this kind you should get a new reference (and check whether it equals null) or subscribe on the event of the object replacement.
  • Long-living objects. Such objects are created upon the first request, by the class that is responsible for this process and exist all the time the application is being executed. It is allowed to store the reference to the object like this.

Detailed Description

SessionState is the short-living object repository. ServiceLocator is the long-living object provider. It creates objects and stores them using Registry. If A needs an instance if B, it requests it either from SessionState or from ServiceLocator, depending on the object kind. If it is needed to get some other objects to create the instance of B, they are requested either from SessionState or from the ServiceLocator. Application classes depend on SessionState and ServiceLocator classes, mutual dependencies between application classes are eliminated. At the same time SessionState and ServiceLocator classes are still change resistant because they do not implement any business logic except the object storage and creation. You are not obliged to pass all objects by the call stack, because objects become to be globally accessible. At the same time, the rules defined exclude situations when out-of-date object references appear. All the classes interested in the short-living object replacement can be notified using events. High-level objects take no more responsibility for low-level object creation or storage. Long-living objects are created upon the first request. The request order determines the actual creation order. The complex initialization code is being put in ServiceLocator methods. ServiceLocator is responsible for choosing the specific class to create an instance of. Short-living objects can be replaced easily, with notification. If you do need to replace the long-living object you should use the long-living wrapper to store an actual class instance. When you recreate an instance, you don't re-create a wrapper.

Implementation

Here is an example of thread-unsafe implementation.

SessionState class implementation example

/// <summary>
/// Provides an information about current session state.
/// </summary>
public class SessionState : ISessionState
{
       #region Class Members

       /// <summary>
       /// Current project file path.
       /// </summary>
       private string _filePathCurrent = null;

       /// <summary>
       /// Current panel used to show the item description.
       /// </summary>
       private Panel _descriptionPanel;

       /// <summary>
       /// The label that used to show number of subnodes in the current node.
       /// </summary>
       private ToolStripStatusLabel _numberOfSubnodesLabel;

       #endregion Class Members


       #region ISessionState Events

       /// <summary>
       /// The event that is fired after the DescriptionPanel substitution.
       /// </summary>
       public event OnDescriptionPanelSubstitutedEventHandler
              DescriptionPanelSubstituted;

       #endregion ISessionState Event


       #region ISessionState Properties

       /// <summary>
       /// Gets or sets the current project file path.
       /// </summary>
       public string FilePathCurrent
       {
              get
              {
                     return _filePathCurrent;
              }
              set
              {
                     _filePathCurrent = value;
              }
       }

       /// <summary>
       /// Gets or sets the current panel used to show the item description.
       /// </summary>
       public Panel DescriptionPanel
       {
              get
              {
                     return _descriptionPanel;
              }
              set
              {
                     _descriptionPanel = value;
                     FireDescriptionPanelSubstituted();
              }
       }

       /// <summary>
       /// Gets or sets the label that used to show number
       /// of subnodes in the current node.
       /// </summary>
       public ToolStripStatusLabel NumberOfSubnodesLabel
       {
              get
              {
                     return _numberOfSubnodesLabel;
              }
              set
              {
                     _numberOfSubnodesLabel = value;
              }
       }

       #endregion


       #region Private Methods

       private void FireDescriptionPanelSubstituted()
       {
              if (DescriptionPanelSubstituted != null)
              {
                     DescriptionPanelSubstituted(this, new EventArgs());
              }
       }

       #endregion Private Methods
}

ServiceLocator class implementation example

/// <summary>
/// Singleton class that provides objects with other objects.
/// It uses the registry to store objects and/or creates objects.
/// </summary>
public sealed class ServiceLocator
{
       #region Class Members

       /// <summary>
       /// Registry to store objects.
       /// All objects that are stored in the registry are considered to be valid
       /// and the same all time during the session.
       /// </summary>
       private Registry _registry = null;

       /// <summary>
       /// The single instance.
       /// </summary>
       private static ServiceLocator _self = null;

       #endregion Class Members


       #region Constructors

       /// <summary>
       /// Initializes a new instance of ServiceLocator class.
       /// </summary>
       private ServiceLocator()
       {
              _registry = new Registry();
       }

       /// <summary>
       /// Performs static initialization of ServiceLocator class.
       /// </summary>
       static ServiceLocator()
       {
              _self = new ServiceLocator();
       }

       #endregion Constructors


       #region Static Methods

       /// <summary>
       /// Returns the active instance of ServiceLocator object.
       /// <example>
       /// This code is recommended to get the active instance:
       /// <c> ServiceLocator serviceLocator = ServiceLocator.GetActive(); </c>
       /// </example>
       /// </summary>
       /// <returns>The active instance of ServiceLocator object.</returns>
       public static ServiceLocator GetActive()
       {
              return _self;
       }

       #endregion Static Methods


       #region IServiceLocator Properties

       /// <summary>
       /// Gets the instance of command processor.
       /// </summary>
       public ICommandProcessor CommandProcessor
       {
              get
              {
                     if (_registry.CommandProcessor == null)
                     {
                            _registry.CommandProcessor = new CommandProcessor();
                     }

                     return _registry.CommandProcessor;
              }
       }

       /// <summary>
       /// Gets the instance of sorting order provider.
       /// </summary>
       public ISortingOrderProvider SortingOrderProvider
       {
              get
              {
                     Configuration20 config = new Configuration20();
                     SortingType sortingType = config.SortingType;
                     switch (sortingType)
                     {
                            case SortingType.Alphabetic:
                                   return new UndefinedSortingOrderProvider();
                            case SortingType.Predefined:
                                   return new HardcodedSortingOrderProvider();
                            default:
                                   return new HardcodedSortingOrderProvider();
                     }
              }
       }

       /// <summary>
       /// Gets the instance of session state.
       /// </summary>
       public ISessionState SessionState
       {
              get
              {
                     if (_registry.SessionState == null)
                     {
                            _registry.SessionState = new SessionState();
                     }

                     return _registry.SessionState;
              }
       }

       /// <summary>
       /// Gets the instance of change manager.
       /// </summary>
       public IChangeManager ChangeManager
       {
              get
              {
                     if (_registry.ChangeManager == null)
                     {
                            _registry.ChangeManager = new ChangeManager();
                     }

                     return _registry.ChangeManager;
              }
       }

       #endregion IServiceLocator Properties
}

Registry class implementation example

/// <summary>
/// Provides some objects with other objects.
/// </summary>
public class Registry
{
       #region Private Members

       private ICommandProcessor _commandProcessor = null;
       private ISessionState _sessionState = null;
       private IChangeManager _changeManager = null;

       #endregion Private Members


       #region Properties

       /// <summary>
       /// Gets or sets the instance of command processor.
       /// </summary>
       public ICommandProcessor CommandProcessor
       {
              get
              {
                     return _commandProcessor;
              }
              set
              {
                     _commandProcessor = value;
              }
       }

       /// <summary>
       /// Gets or sets the instance of sorting order provider.
       /// </summary>
       public ISessionState SessionState
       {
              get
              {
                     return _sessionState;
              }
              set
              {
                     _sessionState = value;
              }
       }

       /// <summary>
       /// Gets or sets the instance of change manager.
       /// </summary>
       public IChangeManager ChangeManager
       {
              get
              {
                     return _changeManager;
              }
              set
              {
                     _changeManager = value;
              }
       }

       #endregion Properties
}

Sources

  1. Martin Fowler's article "Inversion of Control Containers and the Dependency Injection pattern".
© Artem Kondratyev 2007