Common Bits&Bytes Patterns - Factory Pattern, Part 5 of 5 - Willy-Peter Schaub's Cave of Chamomile Simplicity

Common Bits&Bytes Patterns - Factory Pattern, Part 5 of 5

Continued from http://dotnet.org.za/willy/archive/2008/05/20/common-bits-amp-bytes-patterns-iterator-pattern-part-4-of-5.aspx.

Factory Pattern

The last of this short series is the factory pattern, whereby we will cover the Abstract Factory and the Factory Method in future posts.

Category Creational design pattern
Intent Define a common interface for object creation, delegating the instantiation of object to sub classes
Applicability Use when a solution needs independence of how products are created

Class Diagram

image

Source Code Example (from MSDN)

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5:  
   6: namespace Factory
   7: {
   8:     interface IProduct
   9:     {
  10:         int FeatureA();
  11:         int FeatureB();
  12:     }
  13:  
  14:     class ConcreteProductA : IProduct
  15:     {
  16:         int IProduct.FeatureA()
  17:         {
  18:             throw new NotImplementedException();
  19:         }
  20:  
  21:         int IProduct.FeatureB()
  22:         {
  23:             throw new NotImplementedException();
  24:         }
  25:     }
  26:  
  27:     class ConcreteProductB : IProduct
  28:     {
  29:         int IProduct.FeatureA()
  30:         {
  31:             throw new NotImplementedException();
  32:         }
  33:  
  34:         int IProduct.FeatureB()
  35:         {
  36:             throw new NotImplementedException();
  37:         }
  38:     }
  39:  
  40:     interface IAbstractFactory
  41:     {
  42:         IProduct makeProduct();
  43:     }
  44:  
  45:     class ConcreteFactoryA : IAbstractFactory
  46:     {
  47:         public IProduct makeProduct()
  48:         {
  49:             return new ConcreteProductA();
  50:         }
  51:     }
  52:  
  53:     class ConcreteFactoryB : IAbstractFactory
  54:     {
  55:         public IProduct makeProduct()
  56:         {
  57:             return new ConcreteProductB();
  58:         }
  59:     }
  60:  
  61: }

Code Complexity Code Metrics

image 

A final example ...

The final example is an extract from our open source toolbox. It shows a simple factory, which loads .NET assemblies based on configuration (i.e. loosely coupled) and instantiates the services contained within the assembly using .NET Reflection and a common interface to load and instantiate the service. Thereafter the use of the strategy pattern ensure that each instantiated service is encapsulated, interchangeable and differs only in actual behaviour.

   1: using System;
   2: using System.Collections;
   3: using System.Reflection;
   4: using System.Text;
   5: using Microsoft.Practices.EnterpriseLibrary.Configuration;
   6: using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
   7:  
   8: namespace XXX.Toolbox.Services
   9: {
  10:     /// <summary>
  11:     /// The service factory allows callers to instantiate any configured service.
  12:     /// </summary>
  13:     /// <remarks>
  14:     /// This class utilises the Enterprise Library for both its configuration and
  15:     /// exception handling and the following is therefore a prerequisite.
  16:     /// Configuration Application Block Configuration Section named "ServiceFactory", 
  17:     /// using Serializer Transformer and XML File Storage Provider, pointing to the 
  18:     /// servicefactory.config file.ServiceFactory.config must be configured to define 
  19:     /// all services to be hosted by the service factory. Running the ServiceFactoryTest 
  20:     /// console application with the /i runtime option will create an example file.     
  21:     /// Exception Application Block Exception Policy named "Service Factory Policy".
  22:     /// Firstly ensure that an Exception type is defined as part of the above policy, 
  23:     /// with a logging handler and a wrap handler, as well as a "NotifyRethrow" action. 
  24:     /// The wrap handler must specify the ServiceFactoryException as defined in 
  25:     /// ServiceFactory.dll. Secondly define a ServiceFactoryException type, with a 
  26:     /// logging handler and a "NotifyRethrow" action.
  27:     /// See App.config and ServiceFactory.config files shipped with
  28:     /// the ServiceFactoryTest console application for more details.
  29:     /// </remarks>
  30:     public sealed class ServiceFactory : ProviderFactory, IDisposable
  31:     {
  32:         #region Constructors
  33:         /// <summary>
  34:         /// Service factory constructor
  35:         /// Calls second constructor with current configuration context
  36:         /// </summary>
  37:         private ServiceFactory() : this(ConfigurationManager.GetCurrentContext())
  38:         {
  39:         }
  40:  
  41:         /// <summary>
  42:         /// Calls base constructor of FactoryProvider with specified configuration context
  43:         /// </summary>
  44:         /// <param name="context"></param>
  45:         private ServiceFactory(ConfigurationContext context) : base("Service Factory", context, typeof(FactoryConfigProvider))
  46:         {
  47:         }
  48:  
  49:         /// <summary>
  50:         /// Destructor
  51:         /// </summary>
  52:         ~ServiceFactory()
  53:         {
  54:             Dispose(false);
  55:         }
  56:         #endregion
  57:  
  58:         #region IDisposable Members
  59:         /// <summary>
  60:         /// Dispose method, which releases all resources used by the object.
  61:         /// </summary>
  62:         public void Dispose()
  63:         {
  64:             this.Dispose ( true );
  65:         }
  66:         
  67:         /// <summary>
  68:         /// Releases the unmanaged resources used by the object and optionally releases the managed resources
  69:         /// </summary>
  70:         /// <param name="disposing">Indicator whether called by destructor (false) or custom (true).</param>
  71:         public void Dispose ( bool disposing )
  72:         {
  73:             if ( disposing ) GC.SuppressFinalize(this);
  74:                             
  75:             // Cull Cache Items
  76:             IDictionaryEnumerator myEnumerator = singletonHashTable.GetEnumerator();
  77:             while ( myEnumerator.MoveNext() )
  78:             {
  79:                 IDisposable serviceInstance = myEnumerator.Value as IDisposable;
  80:                 if ( null != serviceInstance ) serviceInstance.Dispose();
  81:             }
  82:          
  83:             // Cull Cache
  84:             singletonHashTable.Clear();
  85:         }
  86:         #endregion
  87:  
  88:         #region Instance
  89:         /// <summary>
  90:         /// Service Factory Instance Property
  91:         /// </summary>
  92:         public static ServiceFactory Instance
  93:         {
  94:             get
  95:             {
  96:                 if ( null == instance )
  97:                 {
  98:                     lock ( synchRoot )
  99:                     {
 100:                         if ( null == instance )
 101:                             instance = new ServiceFactory();
 102:                     }
 103:                 }
 104:                 return ( instance);
 105:             }
 106:         }
 107:         #endregion
 108:  
 109:         #region Methods
 110:         /// <summary>
 111:         /// Locator method, which determines if service is in the known configuration and if yes, whether
 112:         /// it has already been loaded as a singelton.
 113:         /// </summary>
 114:         /// <param name="name">Name of service</param>
 115:         /// <param name="serviceConfig">Service configuration if found</param>
 116:         /// <param name="serviceReference">Service reference if loaded as a singleton</param>
 117:         private void LocateService ( string name, out FactoryServiceConfig serviceConfig, out object serviceReference )
 118:         {
 119:             bool bFound           = false;
 120:             serviceConfig    = null;
 121:             serviceReference = null;
 122:  
 123:             providerData = (FactoryConfigProvider)this.CreateInstance(FactoryConfig.SectionName);
 124:  
 125:             // We scan the configuration first, because the singelton state may have changed in the interim
 126:             foreach (FactoryServiceConfig service in providerData.GetFactoryConfig().FactoryServiceConfig)
 127:             {
 128:                 if ( 0 == service.Name.ToLower(System.Globalization.CultureInfo.CurrentCulture).CompareTo(name.ToLower(System.Globalization.CultureInfo.CurrentCulture)) )
 129:                 {
 130:                     serviceConfig = service;
 131:                     bFound          = true;
 132:                     break;
 133:                 }
 134:             }
 135:  
 136:             // Scan the cache if we are OK
 137:             if ( ( true == bFound ) && ( true == serviceConfig.Singleton ) )
 138:             {
 139:                 if ( true == singletonHashTable.ContainsKey(name) )
 140:                 {
 141:                     serviceReference = singletonHashTable[name];
 142:                 }
 143:             }
 144:             else
 145:                 if ( false == bFound )
 146:             {
 147:                 throw ServiceFactoryException.Create ( ExceptionCategory.ServiceNotFound, name, null );
 148:             }
 149:         }
 150:  
 151:         /// <summary>
 152:         /// Helper method to format the public key and public key token in hexadecimal format
 153:         /// </summary>
 154:         /// <param name="assembly">Assembly from which keys are to be extracted</param>
 155:         /// <param name="publicKey">Hexadecimal display of public key</param>
 156:         /// <param name="publicKeyToken">Hexadecimal display of public key token</param>
 157:         private static void GetKeyInformation ( Assembly assembly, out string publicKey, out string publicKeyToken )
 158:         {
 159:             StringBuilder key   = new StringBuilder();
 160:             StringBuilder token = new StringBuilder();
 161:  
 162:             publicKey           = null;
 163:             publicKeyToken      = null;
 164:  
 165:             byte[] publicKeyBytes = assembly.GetName().GetPublicKey();
 166:             if ( null != publicKeyBytes )
 167:             {
 168:                 for ( int i=0; i < publicKeyBytes.GetLength(0); i++ ) key.AppendFormat( "{0:x2}", publicKeyBytesIdea ); 
 169:                 publicKey      = key.ToString();
 170:             }
 171:                 
 172:             byte[] publicKeyTokenBytes = assembly.GetName().GetPublicKeyToken();
 173:             if ( null != publicKeyTokenBytes )
 174:             {
 175:                 for (int i=0; i < publicKeyTokenBytes.GetLength(0); i++) token.AppendFormat( "{0:x2}", publicKeyTokenBytesIdea );
 176:                 publicKeyToken = token.ToString();
 177:             }
 178:         }
 179: