Jump to content

C# Programming/Design Patterns

From Wikibooks, open books for an open world

Design Patterns are common building blocks designed to solve everyday software issues. Some basic terms and example of such patterns include what we see in everyday life. The design patterns are broadly classified into 3 types:

1. Creational: These patterns are designed for class instantiation. They can be either class-creation patterns or object-creational patterns.

2. Structural: These patterns are designed with regard to a class's structure and composition. The main goal of most of these patterns is to increase the functionality of the class(es) involved, without changing much of its composition.

3. Behavioral: These patterns are designed depending on how one class communicates with others.


Factory Pattern

[edit | edit source]

The factory pattern is a method call that uses abstract classes and its implementations, to give the developer the most appropriate class for the job.

Lets create a couple of classes first to demonstrate how this can be used. Here we take the example of a bank system.

public abstract class Transaction
{
     private string _sourceAccount;

     // May not be needed in most cases, but may on transfers, closures and corrections.
     private string _destinationAccount;
   
     private decimal _amount;
     public decimal Amount { get { return _amount; } }
   
     private DateTime _transactionDate;
     private DateTime _effectiveDate;

     public Transaction(string source, string destination, decimal amount)
     {
          _sourceAccount = source;
          _destinationAccount = destination;
          _amount = amount;
          _transactionDate = DateTime.Now;
     }

     public Transaction(string source, string destination, decimal amount, DateTime effectiveDate) : this(source, destination, amount)
     {
          _effectiveDate = effectiveDate;
     }

     protected decimal AdjustBalance(string accountNumber, decimal amount)
     {
          decimal newBalance = decimal.MinValue;

          using(Mainframe.ICOMInterface mf = new Mainframe.COMInterfaceClass())
          {
               string dateFormat = DateTime.Now.ToString("yyyyMMdd HH:mm:ss");
               mf.Credit(dateFormat, accountNumber, amount);
               newBalance = mf.GetBalance( DateTime.Now.AddSeconds(1), accountNumber);
          }
          
          return newBalance;
     }
     
     public abstract bool Complete();
}

This Transaction class is incomplete, as there are many types of transactions:

  • Opening
  • Credits
  • Withdrawals
  • Transfers
  • Penalty
  • Correction
  • Closure

For this example, we will take credit and withdrawal portions, and create classes for them.

public class Credit : Transaction
{
     // Implementations hidden for simplicity

     public override bool Complete()
     {
          this.AdjustBalance( _sourceAccount, amount);
     }
}

public class Withdrawal : Transaction
{
     // Implementations hidden for simplicity

     public override bool Complete()
     {
          this.AdjustBalance( _sourceAccount, -amount);
     }
}

The problem is that these classes do much of the same thing, so it would be helpful, if we could just give it the values, and it will work out what class type we require. Therefore, we could come up with some ways to distinguish between the different types of transactions:

  • Positive values indicate a credit.
  • Negative values indicate a withdrawal.
  • Having two account numbers and a positive value would indicate a transfer.
  • Having two account numbers and a negative value would indicate a closure.
  • etc.

So, let us write a new class with a static method that will do this logic for us, ending the name Factory:

public class TransactionFactory
{
     public static Transaction Create( string source, string destination, decimal amount )
     {
          if(!string.IsNullOrEmpty(destination) )
          {
               if(amount >= 0)
                    return new Credit( source, null, amount);
               else
                    return new Withdrawal( source, null, amount);
          }
          else
          {
               // Other implementations here
          }
     }
}

Now, you can use this class to do all of the logic and processing, and be assured that the type you are returned is correct.

public class MyProgram
{
     static void Main()
     {
          decimal randomAmount = new Random().Next()*1000000;
          Transaction t = TransactionFactory.Create("123456","",randomAmount);
          // t.Complete(); <-- This would carry out the requested transaction.
 
          Console.WriteLine("{0}: {1:C}",t.GetType().Name, t.Amount);
     }
}

Singleton

[edit | edit source]

The singleton pattern instantiates only 1 object, and reuses this object for the entire lifetime of the process. This is useful, if you wish the object to maintain state, or if it takes lots of resources to set the object up. Below is a basic implementation:

public class MySingletonExample
{
   private static object obj = new object();
   private volatile static Hashtable _sharedHt;

   public static Hashtable Singleton
   {
     get 
      {
         if(_sharedHt == null){
             lock(obj){
                  if(_sharedHt == null){
                     _sharedHt = new Hashtable();
                  }
             }
         }
         return _sharedHt;
      }
      // set { ; }
     // Not implemented for a true singleton
   }

   // Class implementation here..
}

The Singleton property will expose the same instance to all callers. Upon the first call, the object is initialised and on subsequent calls this is used.

Examples of this pattern include:

  • ConfigurationSettings (Generic settings reader)
  • HttpApplication (Application object in ASP .NET)
  • HttpCacheUtility (Cache object in ASP .NET)
  • HttpServerUtility (Server object in ASP .NET)