C# Programming/Design Patterns
Cover | Introduction | Basics | Classes | Advanced Topics | The .NET Framework | Index
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.
publicabstractclassTransaction { privatestring_sourceAccount; // May not be needed in most cases, but may on transfers, closures and corrections. privatestring_destinationAccount; privatedecimal_amount; publicdecimalAmount{get{return_amount;}} privateDateTime_transactionDate; privateDateTime_effectiveDate; publicTransaction(stringsource,stringdestination,decimalamount) { _sourceAccount=source; _destinationAccount=destination; _amount=amount; _transactionDate=DateTime.Now; } publicTransaction(stringsource,stringdestination,decimalamount,DateTimeeffectiveDate):this(source,destination,amount) { _effectiveDate=effectiveDate; } protecteddecimalAdjustBalance(stringaccountNumber,decimalamount) { decimalnewBalance=decimal.MinValue; using(Mainframe.ICOMInterfacemf=newMainframe.COMInterfaceClass()) { stringdateFormat=DateTime.Now.ToString("yyyyMMdd HH:mm:ss"); mf.Credit(dateFormat,accountNumber,amount); newBalance=mf.GetBalance(DateTime.Now.AddSeconds(1),accountNumber); } returnnewBalance; } publicabstractboolComplete(); }
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.
publicclassCredit:Transaction { // Implementations hidden for simplicity publicoverrideboolComplete() { this.AdjustBalance(_sourceAccount,amount); } } publicclassWithdrawal:Transaction { // Implementations hidden for simplicity publicoverrideboolComplete() { 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
:
publicclassTransactionFactory { publicstaticTransactionCreate(stringsource,stringdestination,decimalamount) { if(!string.IsNullOrEmpty(destination)) { if(amount>=0) returnnewCredit(source,null,amount); else returnnewWithdrawal(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.
publicclassMyProgram { staticvoidMain() { decimalrandomAmount=newRandom().Next()*1000000; Transactiont=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:
publicclassMySingletonExample { privatestaticobjectobj=newobject(); privatevolatilestaticHashtable_sharedHt; publicstaticHashtableSingleton { get { if(_sharedHt==null){ lock(obj){ if(_sharedHt==null){ _sharedHt=newHashtable(); } } } 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)