Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Design Principles: An Illuminati for Better Solution

4.70/5 (20 votes)
20 Mar 2011CPOL10 min read 41.7K   86  
From my earliest memory of programming, I was taught that we should do some level of design before coding. Somewhere around the way I started hearing phrases Dependency Injection, IoC etc., but whenever I asked people the need for these patterns, I seldom got an answer that satisfied me…

Introduction

This article is not for experts. Rather, in this canvas, I have tried to demonstrate the power of good design for those who are puzzled among the various jargons of the design principles.

Here, I will start with a simple problem statement… then provide a basic solution (without considering any design principle)… discuss the problem in the solution… finally develop a better solution by applying design principles, one by one.

A Request to the Reader

Before reading each of the solutions, do some brainstorming session. Maybe you would be able to find out a better solution for the problem.

Other Article

For primitive AJAX, click here.

Scenario

NS-Corporation (Nano Soft Corporation) is a leading solution provider organization that provides solutions across various domains worldwide. One of the renowned analysts Mr. Flemming visited their prospective client “Best Insurance Ltd.” to understand their existing business scenario. We will see how NS-Corporation team reached the Design Conclusion.

Problem Statement

From Client’s Desk – “We are running an Insurance company. We have a number of shareholders and we need to prepare monthly statements for them. We provide our monthly statements to our shareholders in PDF format. As an input for generating those statements, we need a number of Excel files, in a specific format, which are provided by our partner companies. Presently our employees read those Excel files and do some calculations and business processing and finally prepare the PDF files for each of our shareholders.

We need an automated system which will do these activities for us.

Solution

The solution would be developed in an iterative way. At the end of each iteration, the limitation of aforementioned solution would be discussed.

Before we start, let’s see how Mr. Flemming abstracted the problem…

He said, “The abstract is Reading – Business Processing – Writing.

Iteration One

When the abstract of the statement is presented to “Manoj”, one of the leading programmers of NS-Corporation, he instantly replied, “Hey this is probably the easiest problem I ever faced in my programming career… Just write a class with three methods - Read, Process and Write.

So the solution provided by him is depicted as follows:

Fig1.png

Bannhi who was leading Manoj, hardly few feet above the ground… in short very short… but is having some deep rooted insight on design principles, she scratched her head and argued “We should not club Business Process along with others which are not Business Operations. Because in that case a small change in any of the Business/Non-Business areas may force change in others and we need to run the total test cycle for each change.” – She was highly supported by testers, Sonali and Ambuja.

Iteration Two

So she presented her model:

Fig2.png

The complete logic as per her opinion is depicted as follows:

  1. Detach Functional (Business) Requirements from the Non-Functional one, so that change in one area won’t force running complete test cycle.
  2. Try to design Classes or Function with a Single Responsibility (Single Responsibility Principle - SRP).This is the reason for designing three classes having Reading responsibility, processing responsibility and Writing responsibility.

[Motivation of SRP: This principle states a single class should have only one reason to change. For example, if we have 2 reasons to change a single class, we should split the functionality into two classes.]

Now everybody looks happy.

Cafeteria Session:

Malini & JD, the database experts of NS-Corporation wish the project team in a coffee session but look a bit disappointed since this project does not have any database interaction.

JD is a cool guy, having lots of humor embedded which is recently shadowed by the recession-cloud. He also is the owner of very spacious forehead which almost wins the battle of touching his collar!!!

Malini is very famous for her cool nature… So cool that one should always avoid taking the risk of making her disappointed!!!

Aditya was the epicenter of the entire project, THE PROJECT MANAGER.

Being a PM, Aditya was taught to sense the upcoming risk…
He foresaw, “Prolonged disappointment of Malini could be disastrous…”So he decided to unfold some more facts about the project.
Team there is good news…” – Aditya immediately got the attention.
“We are going to have database interaction extension for this project.” – he announced.

Now the Team members in the cafeteria wished each other and celebrating the moment. Everybody looks happy… except one.

Iteration Three

Sayan, always lost in his own world suddenly wake up and announced “We need to change our Design”. Bannhi looked at him with raised eyebrows. But Manoj looked very happy.

Let us know the “why” from the horse’s mouth.

The design Bannhi suggested seems good. But now we need to have a look at the future. Since our client committed to give a second phase of the project which is having the database interaction too, therefore we should think about the extension capabilities of our solution.
Let us take an example of Read () operation. Now it seems that we need to read from Excel files as well as DB tables.” –
Sayan Paused.

But we can easily accomplish it by having an Input parameter ‘readFrom’ in the Read () method. And implement the method accordingly. Client will call the method with appropriate parameter!!!” – Bannhi argued.

Correction…” – Sayan exclaimed!!! “What if, tomorrow client asks us to read from a Word file also… and then from a PDF file… then from a DJVW file? Each time we need to incorporate the change into our ‘ReadPartnerDocument’ class and need to test the whole stuff. Moreover, is it not violation of ‘Open Close Principle’ design principle in some way?” – This time test gals turn their support to Sayan.

So Sayan presented his design:

Fig3.png

The Process and Write part of the diagram is omitted for simplicity

The design diagram was having two noticeable parts. One is the introduction of interface (Generally known as Dependency Inversion) and the 2nd is the introduction of “DocumentUtility” class. The benefit of this design is depicted below:

  1. For reading each type of document, we do not need to disturb the already existing tested functionalities. For example, say in “Phase One”, we built and tested “ReadPartnerXLDocument”. In “Phase Two”, we need to read from DB also. In the present scenario, we do not need to disturb our existing tested implementation “ReadPartnerXLDocument”. Rather we will implement a new class “ReadPartnerDBTable” which will take care of the reading from database, and so on… Hence we are not disturbing the existing implementation and take away the necessity of re-testing of that part.
  2. A new class named “DocumentUtility” is introduced, which will contain the common functionalities across the various types of document readers.
    Actually there are two schools of thought. One prefers the common functionality in a base class and enforces inheritance relationship. And the other likes to implement the common functionality into a utility class and enforces Aggregation relationship. Personally I support Sayan’s aggregation model, because Inheritance model is tightly coupled by nature. But in case anybody is sure about no deep inheritance in future, then she/he can implement the same by inheritance.

[Motivation of Open-Close Principle: This principle states that the design of class should be done in a way that new functionality can be added by doing no changes in the existing code. That means the design should be open to adopt new functionality but closed for modifying existing functionality for adopting that new one. In short, for incorporating a new functionality there, it would not be required to modify the existing functionalities.]

I see…” – Aditya said, perhaps not seeing at all.

But this needs some review from the outside team…” – He suggested.

But his suggestion somehow ignited Manoj!!!
This fellow always has lesser faith on us…” – Manoj groaned.

ManoOj…” – Aditya chose his only casualty.

Yeeaas AAA..Aditya-Da” – Manoj replied with bewilderment that how could Aditya figure out his comment.

Please send the design document for review to Nilanjan and Sudipta…” – Aditya ordered.

With all his frustration, Manoj followed the order of his tormentor and informed Nilanjan and Sudipta.

Iteration Four

The design seems to be perfect to Nilanjan. But he showed his concern regarding the creation of Reader-Objects in client program. He argued, “Let us imagine one of our client programs is currently reading from an Excel document. Tomorrow it may need to read from a PDF document instead of the Excel one. In that case, if we create an object directly in the client program in a hardcoded fashion, then it will force us to re-compile our client. So we should have an object creation container where we will inject the required object from outside, may be from a configuration file.”

So he associates a DependencyResolver with the client.

Fig4.png

He suggested, “… objects should not be created directly into the client, rather they should be created through DependencyResolver so that we can control the creation of it from outside (e.g., from config file).”

To the Reader

Motivation of Dependency Injection: Dependency Injection is a general concept. To know this in detail, one should refer to Martin Fowler’s (http://www.martinfowler.com/articles/injection.html.) article. The intention of this concept is to use a dependent class without directly referencing that class. As described in the iteration four, Dependency-Injection allows us to associate objects in a loosely coupled fashion. Full code for implementation is available for download.

Food for Thought: Imagine real life scenarios where this solution could be helpful/obsolete.

Sudipta seems a bit unhappy with the design (nobody minds, because most of the time he remains unhappy with his own work!!!).
But he pointed out a valid issue “If we need to insert a policy before or after the execution of any method for some good reasons, then we need to alter our existing code base. For example, if we need to find how much time is taken for executing the Read () method, for some performance tuning then we need to change codebase. Moreover how one can control the lifetime of the objects created? Would it be possible to change the lifetime of configurable objects from outside? How we separate the cross cutting concerns like logging, from main application code?

Iteration Five

So he introduced IoC container in place of “DependencyResolver” into the design. So the design suggested by him is presented below:

Fig5.png

Motivation for Inversion Of Control – The Wiki definition of IoC is as follows, “Inversion of control, or IoC, is an abstract principle describing an aspect of some software architecture designs in which the flow of control of a system is inverted in comparison to procedural programming.”. Normally we use library code by calling the corresponding library function e.g., Console.WriteLine(). But sometimes Framework needs to call you back. For example, writing a custom array sort algorithm using IComparable interface. The code is depicted as follows:

C#
static void Main(string[] args)
{
            // ComplexNumber class defines custom sort order where sort will 
            // takes place by real numbers and then by imaginary numbers.
            ComplexNumber[] arr = new ComplexNumber[11];

            arr[0] = new ComplexNumber(-1, 2);
            arr[1] = new ComplexNumber(1, 3.5);
            arr[2] = new ComplexNumber(2.5, 2);
            arr[3] = new ComplexNumber(-2.5, 4);
            arr[4] = new ComplexNumber(-3, 6);
            arr[5] = new ComplexNumber(3, 6);
            arr[6] = new ComplexNumber(0, 5);
            arr[7] = new ComplexNumber(0, 5);
            arr[8] = new ComplexNumber(0, -5);
            arr[9] = new ComplexNumber(2, 0);
            arr[10] = new ComplexNumber(0.5, -25);

            // Array.Sort is calling the CompareTo method in ComplexNumber. 
            // Inversion of control happens here.

            Array.Sort(arr);

            foreach (ComplexNumber c in arr)
                Console.WriteLine(c.ToString());

            Console.ReadLine();
} 

internal class ComplexNumber : IComparable
{
            double _realPart = 0;
            double _imaginaryPart = 0;
            
            public ComplexNumber(double realPart, double imaginaryPart)
            {
                _realPart = realPart;
                _imaginaryPart = imaginaryPart;
            }

            // This function compares real parts and then imaginary parts
            public int CompareTo(object obj)
            {
                ComplexNumber otherValue = (ComplexNumber)obj;

                if (_realPart == otherValue._realPart)
                {
                    if (_imaginaryPart == otherValue._imaginaryPart)
                        return 0;
                    else if (_imaginaryPart > otherValue._imaginaryPart)
                        return 1;
                    else 
                        return -1;
                }
                else if (_realPart > otherValue._realPart)
                {
                    return 1;
                }
                else
                {
                    return -1;
                }
            }

            public override string ToString()
            {
                return _realPart.ToString() + " + " + _imaginaryPart.ToString() + "j";
            }
}

The basics of Inversion-Of-Control is presented above. The IoC-Container is based on this principle and clubbed with several other patterns. In this example, IoC is working almost like dependency injection with some extra features mentioned above.
Hummm… so this is the design you all agreed, right” – Aditya asked.
Yes” – Everybody nodded.
Check guys, if you need to refine it more, we can do that…we still have time.

Nobody in NS-Corporation could refine it more… But Aditya still has time… he is still waiting...

If anyone of you can refine that design, please send him an email at WaitingAditya@hotmail.com!!!

Acknowledgement

In this article, I have tried to demonstrate the way to conceive a good design. The main purpose of this article is to present the logical reasons behind choosing a specific design. In my next article, I would dig into the patterns mentioned here.

The participants of this article are my friends in real life. So in many places in this article, I have digged them… but that is only for fun and I feel that I have the right to do this with my closer ones.

All of them have an unquestionable level of efficiency and are very capable of reaching into the final design with a single iteration. I am very lucky to have these stalwarts as my friend.

One more thing I should say is that this writing is very much inspired by Aditya’s Treasure Hunt (A legendary episode… I must say) & his renowned Whitepapers (We heard about those so many times… but unfortunate enough to see any one of them!!!).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)