When I first started designing and architecting online applications, we decided to move custom settings from a database to an XML file for more versatile settings for our customers. With hundreds of agencies using our system, we needed to have the ability to easily and quickly change settings on a customers website without making core-code changes, XML allowed this. However, a year after we released we found issues with this setup.
The setup it self was solid, however we didn’t expect the needs of our customers to be so… unique.
Enter the 80/20 rule.
Where once a button click would say print a Circle on a screen, we found soon that 20% of our customers wanted that to be an oval – so we bite the bullet and make a few code changes to accept more settings and voila, a customer can either have an oval or a circle.
Oh, if it was that easy! Soon, every customer now wanted something different. A Triangle, square, 3D Sphear, dishwasher, and some wanted a few Ninjas! Come on! Ninjas are rare and priceless, we can’t just give thos…. sorry I digressed.
What what to do? Do we throw in 100 conditional statements? Do we build a system that is intelligent enough to almost be self aware to know what our customers want? (Any programmer knows that we need to know what our customers need/want/deserve before they actually know they need/want/deserver it).
So where did we go? The solution was custom libraries for each customer, using reflection at run time to load those libraries up.
You see, the customer who wants a triangle, didn’t care about the sphear, the circle, the ninja. They wanted triangle, pointy edges and all. The same went for other customers, so the answer was clear.
With this implementation, we rarely have to make any core-code changes. Instead, we derive a new library off of a base class, add the functionality needed for our customer, and release it. Plain and simple.
A sample of this will be uploaded shortly.
This project holds the 2 classes I’ll be working with. Each class(factory) has a static method called “Create” which creates the correct Object based on the passed in path.
There are two classes in this project: Greeting and Shape.
These two classes will be the ones overridden by our “factories” to produce custom actions and events.
BicNet.Projects.FactoryExample.Triangle / Square
This is derived from BicNet.Projects.FactoryExample.Base. Greeting and Shape both inherit from Base.Greeting and Base.Shape. This allows me to cast a BicNet.Projects.FactoryExample.Triangle.Shape object as a BicNet.Projects.FactoryExample.Base.Shape, while retaining all the functionality of .Triangle.Shape (Gota love Managed Code).
I’m not going to show BicNet.Projects.FactoryExample.Square because it’s the same as Triangle
So how does this all work. Let’s look at the FactoryExample.Factory project.
1: namespace BicNet.Projects.FactoryExample.Factory
3: public class FGreeting
5: public static BicNet.Projects.FactoryExample.Base.Greeting Create(string path)
7: string className = path + ".Greeting";
8: //Must use System.Reflection.Assembly - can not just use Assembly as the name as
9: //it will not work. Can also use [Assembly] instead
10: return (BicNet.Projects.FactoryExample.Base.Greeting)System.Reflection.Assembly.Load(path).CreateInstance(className);
What’s being passed into the Create(..) method would be: “BicNet.Projects.FactoryExample.Square”. What this then does is look for that assembly and return the “Greeting” object from that Library. It will then return it, giving me full control over that object.
1: protected void btnLoadFactory_Click(object sender, EventArgs e)
3: BicNet.Projects.FactoryExample.Base.Shape shape = BicNet.Projects.FactoryExample.Factory.FShape.Create(ddlFactories.SelectedItem.Text);
4: BicNet.Projects.FactoryExample.Base.Greeting greeting = BicNet.Projects.FactoryExample.Factory.FGreeting.Create(ddlFactories.SelectedItem.Text);
6: lblGetShapeName.Text = shape.GetShapeName();
7: lblGetShape.Text = shape.GetCustomText();
8: lblGetGreeting.Text = greeting.GetGreeting();
What’s the Point?
Yes, this is very simple with shapes and such, however what about real world scenarios? One of the main questions I’ve been asked about with this model is:
If you just did market research and you knew what your customers wanted, you wouldn’t need to make a system like this. Why spend the extra time and effort to build a system that’s capable of doing this when you could just do what customers wanted in the first place?
Anyone in the Software industry can tell you customers don’t always know what they want, and customer’s needs change every day / week / year (hour?). And, for those that deal with $$, maintenance on any application is usually the big killer for time (effort) and money.
Another argument might be:
You have 500 customers working on an Item Tracking System (Tasks, to-do’s etc.).
- 300 of those customers want it when they submit an item, for an email to be sent to themselves, and to the people assigned to the task.
- 100 of those customers want 2 sub-tasks created for each Task created, and both those tasks to be automatically assigned to “Mary Smith” in the system.
- 50 of the 100 remaining customers want a PDF created as soon as the Task is created to be saved as a snapshot of the original task.
- 25 of the 100 want any task that is completed to have a PDF snapshot taken, saved in a specific directory so their FTP program can look for them.
- The final 25 customers don’t know what they want, but we are safe because as soon as they come up with whatever customers come up with, we have the ability to plug it in without changing base code because we can just change their factory.
Well pretty much. Yes, you could get the above done with XML – if you knew what you were facing before you started. However, that’s not the problem we face in our industry. We are always changing and always improving. Having the ability to quickly change something for a customer or set of customers without interupting the flow of everything else, is what we want. Maintenance is always the big cost item with Web Applications because of the ongoing changes to it.