|
Now there is something quite serious missing: The ability to retrieve an instance of one of these two child classes.
YOU DO NOT WANT TO INSTANTIATE THESE DIRECTLY. The typical case I see in practice globally is to create a static
'Instance Factory' in the Abstract Parent. Many people call this a Factory as in the Design Pattern 'Factory' or
'Abstract Factory' and that is incorrect. That's another story...
It is not a good practice to add an 'Instance Creating' method (often mistakenly called a 'Factory Pattern', and
typically manifested as a static method living in an Abstract class), that returns a concrete child instance you
want via a hard-coded switch statement (unless you knew there would never be additional children off this parent
class, a rare occurrence, such a Male and Female as children off of Person, but even then you face complexities, as
I have found living in New York (grin)). However the vast majority of developers do just that, by passing in some
kind of identifier, with the switch statement making hard coded instantiations via early binding. The addition of
new children would require a new compilation and deployment to UAT and Production as you would be forced to modify
your hard-coded switch statement (or other conditional logic) and do a completely new deployment to production. You
can avoid all that with the practices I show in this article which are key to achieving ‘Agile’ Development
optimization. You want to be able to add new children and not have the parent have any knowledge of the children it
is supporting. We do this with metadata and reflection, the two immutable cornerstones of expert level .NET
Development.
For example, this is NOT how you should create instances of the CorporateCustomer class (as one example):
CorporateCustomer oCustomer = new CorporateCustomer();
Are there times when you have a simple solution and there will never be more then two children (perhaps you have
‘sex’ abstract parent and ‘male’ and ‘female’ children. You could probably just code these, but you should still
refer in your code to ‘sex’ not the Types male or female as I will describe shortly.
Instead of direct instantiation (something we will actually make impossible later) there is a static method in the
abstract class with the following interface:
public static CustomerAbstract GetCustomer(String CustomerTypeName)
We typically also create an overloaded method which also allows an Assembly Name to be passed in.
So instead of the typical case I see:
CorporateCustomer oCustomer = new CorporateCustomer();
We leverage best practice Object Oriented design and say:
CustomerAbstract oCorporateCustomer = CustomerAbstract.GetCustomer("CSharpPatternsExample.AbstractClassExample.CorporateCustomer");
You are probably thinking 'Refactor'! That Type Name needs to be abstracted as it is so long and hard to remember,
but before I get there let me show you the pattern (you cannot use this in all cases as sometimes you need a class
like this to have a default constructor with no parameters, but it works in many if not most cases). For each child
we add a new Constructor and modify the default constructor (and add a new protected property to the parent):
To be added to the Abstract Parent:
protected bool CreatedFromInstanceFactory = false;
We also add a new constructor for each child:
Then the default constructor becomes:
You can always add a Boolean condition to see if you want to enforce this.
So now when we call:
CorporateCustomer oCustomer = new CorporateCustomer();
We get:
An unhandled exception of type 'System.Exception' occurred in CSharpPatternsExample.exe
Additional information: This child should be instantiated from the static method off the parent called GetCustomer
Very Nice!
So you might be thinking why you returned the type 'CustomerAbstract' from this instantiation method:
CustomerAbstract oCorporateCustomer = CustomerAbstract.GetCustomer("CSharpPatternsExample.AbstractClassExample.CorporateCustomer");
This goes back to the basic statement I made in the beginning about coding to Abstract Classes and Interfaces instead of Concrete classes. Why do we care? Well for one, we have dramatically expanded the flexibility of our application as you will shortly see. We can return other classes we had not anticipated and our code will still work, as we have not bound ourselves to implementation. Instead bind yourself to Interfaces (Abstract Classes or Literal Interfaces). In the original Design Patterns by the ‘Gang of Four’ (GoF) they focus on this but it is Interface Driven. This hurts you as although Interfaces are a critical part of the story, Abstract Classes are often a better choice in C# when implementing patterns.
|