Wely

wel = new Geeks();
See also: Other Geeks@INDC

March 2007 - Posts

A practical "polymorphism" example

In my last post, I described what interface and abstract class are, how to create and use them. But, the question is “Why do we need to use interface or abstract class?”, “Is it useful?”, “What if we don’t use them?”. Actually, the primary reason of using them is we are going to do “Polymorphism”. A term that Object Oriented developer might be familiar, but they seldom implement it in real world project (included me J).

This article also explains one of the refactoring principles “Replace Conditional with Polymorphism”.

I summarized and rewrote the article from Bozeman’s blog in my own word. Hopefully that it will be useful for the reader and also improve my OO skillJ.

Rather that giving some boring definition of polymorphism, let’s do it with a real world example, it will be better. Here’s our story begins:

Sometimes we would create an online application for taking the survey. The questions and answers type in survey are easily changed. Some of them are required the respondents to fill in the textbox. Some of them are multiple choices with single answer, some of them are multiple choices with multiple answer.

From the scenario, we probably will set up with 3 classes:

  • Survey.cs

It will hold all of the questions in our survey. Maybe it will contain the survey’s name, survey’s date, or some methods.

  • Question.cs

It will hold the question and maybe a collection of choices.

  • Choice.cs

This class would be a section of the possible answer. Maybe it could be Text and Value.

With me so far, there is nothing outrageous things here. We have a simple hierarchy of Survey has Questions, and Questions has Choices.

Now, let’s conduct some codes that will make us easier to understand starting with the lowest level, Choice.

    public class Choice

    {

        private string _text;

        private string _value;

        public string Text

        {

            get { return _text; }

            set { _text = value; }

        }       

        public string Value

        {

            get { return _value; }

            set { _value = value; }

        }

        public Choice(string t, string v)

        {

            this._text = t;

            this._value = v;

        }

    }

  

Then, the question class goes here:

  

    public class Question

    {

        private string _questionText;

        private ArrayList _choicesList;

        public string QuestionText

        {

            get { return _questionText; }

            set { _questionText = value; }

        }       

        public ArrayList ChoicesList

        {

            get

            {

                if (_choicesList == null)

_choicesList = new ArrayList();

                return _choicesList;

            }

            set { _choicesList = value; }

        }

        public Question(string q)

        {

            QuestionText = q;

        }

        public override string ToString()

        {

            StringBuilder sb = new StringBuilder(this.QuestionText);

            foreach (Choice ch in ChoicesList)

            {

                sb.Append( String.Format("\n \t {0}", ch.Text));

            }

            return sb.ToString();

        }

    }

  

And the last, survey class would look like this:

  

    public class Survey

    {

        private string _title;

        private ArrayList _question = new ArrayList();

        public string Title

        {

            get { return _title; }

            set { _title = value; }

        }       

        public ArrayList Question

        {

            get { return _question; }

            set { _question = value; }

        }

        public Survey(string t)

        {

            _title = t;

        }

        public override string ToString()

        {

            StringBuilder sb = new StringBuilder(Title);

            for (int ctr = 0; ctr < Question.Count; ctr++)

            {

                sb.Append(String.Format("\n\n{0}. {1}",

ctr+1, Question[ctr]));

            }

            return sb.ToString();

        }

    }

  

OK. Now, you can build your survey application like this:

  

        static void Main(string[] args)

        {

            Question q1 = new Question("Do you like .NET?");

            q1.ChoicesList.Add(new Choice("Yes", "1"));

            q1.ChoicesList.Add(new Choice("No", "0"));

Question q2 = new Question("What is your favorite .NET language?");

            q2.ChoicesList.Add(new Choice("C#", "1"));

            q2.ChoicesList.Add(new Choice("VB.NET", "2"));

            q2.ChoicesList.Add(new Choice("C++", "3"));

            q2.ChoicesList.Add(new Choice("J#", "4"));

            Survey survey1 = new Survey(".NET Survey");

            survey1.Question.Add(q1);

            survey1.Question.Add(q2);

            Console.WriteLine(survey1);

            Console.ReadLine();

        }

  

Everything goes well so far. If the application is the ASP.NET Web Application or Windows Application, radio button is the appropriate choice for the case which is multiple choices, single answer.

Now, imagine that the survey has a new type of question, like fill in the blank (we are going to use textbox), or multiple choices with multiple answer (probably we are using check box). Or maybe the annoying user asks to change the stupid radiobutton to dropdownlist. How do we handle these situations?

Without Polymorphism

  

If we don’t know about polymorphism, we would add a property QuestionType that held what type of question is. Then our ToString() method will contain switch – case statement to see how to display the question. In simple case, it just works.

But, if the following cases occur:

  • We need to construct a PreSelect() method. If the question type is multiple choices, we can only PreSelect one. If the question type is “fill in the blank”, we fill in the textbox. If the question type is multiple answers, we fill in check boxes. Again, we have to use if – else or switch – case statement to select what kind of question type it is.
  • Later on, we decide to display the survey result. If the question type is multiple choices, we will use pie chart to display the result. If the question type is “fill in the blank”, we will display two most common answer. If the question type is multiple answers, we will bind it to bar graph. Again, we have to use the “boring” switch – case statement.
  • Later, we are going to add new question type again like “matching”, true/false, etc. What will happen? We have to go back to all our old methods and add them to the switch statement. This is ugly and annoying. It is very “bar-bar”, not elegant at all, hard to manage and maintain.

  

The following Question class illustrates how to do without using polymorphism:

  

       public class Question

        {

            private string _questionText;

            private ArrayList _choicesList;

            private int _questionType;

            public int QuestionType

            {

                get { return _questionType; }

                set { _questionType = value; }

            }

            public string QuestionText

            {

                get { return _questionText; }

                set { _questionText = value; }

            }

            public ArrayList ChoicesList

            {

                get

                {

                    if (_choicesList == null)

_choicesList = new ArrayList();

                    return _choicesList;

                }

                set { _choicesList = value; }

            }

            public Question(string q)

            {

                QuestionText = q;

            }

            public override string ToString()

            {

                StringBuilder sb = new StringBuilder(this.QuestionText);

                switch (_questionType)

                {

                    case 1: // 1 represent SingleRadio

                        {

                            foreach (Choice answer in ChoicesList)

                            {

                                sb.Append(String.Format("\n<radio>\t\t{0}", answer.Text));

                            }

                        } break;

                    case 2: // 2 represent SingleDropDown

                        {

                            foreach (Choice answer in ChoicesList)

                            {

                                sb.Append(String.Format("\n<dropdownlist>\t\t{0}", answer.Text));

                            }

                        } break;

                    case 3:

                        {

                            foreach (Choice answer in ChoicesList)

                            {

                                sb.Append(String.Format("\n<checkbox>\t\t{0}", answer.Text));

                            }

                        }

                        break;

                }

                return sb.ToString();

            }

            public void PreSelect()

            {

                switch (_questionType)

                {

                    case 1:

Console.WriteLine("You can just choose one of these radio button."); break;

                    case 2:

Console.WriteLine("You can just choose one of the dropdownlist."); break;

                    case 3:

Console.WriteLine("You can just choose more than one check box."); break;

                }

            }

        }

  

With Polymorphism

Luckily, polymorphism comes to offer the better way. Let’s see how to take the advantages of it. We see that all Questions have something that is common / general. They have:

  • The question,
  • Most of them have choices,
  • Ability to preselect the answer,
  • Ability to display the answer.

I know that not all of the question type has choices (such as “fill in the blank”), but make it simple first J, we will include choices in our interface now.

With that in mind, we could create an Interface with these properties and methods. Here’s our interface would looks like:

  

    public interface IQuestion

    {

        string Text

        {

            get;

            set;

        }

        ArrayList Choices

        {

            get;

            set;

        }

        void DisplayAccumulatedResult();

    }

  

What does it do for us? Here, we know that any Question that inherit IQuestion must implements these two properties. We don’t need to use ToString() method because all class inherit the ToString() method from System.Object.

Now from the IQuestion interface, let’s create three classes, one called SingleRadio, one called SingleDropDown, and one called MultiCheckBox.

  

    public class SingleRadio : IQuestion

    {

        public SingleRadio(string t)

        {

            _text = t;

        }

        private string _text;

        private ArrayList _choices = new ArrayList();

        public string Text

        {

            get { return _text; }

            set { _text = value; }

        }       

        public ArrayList Choices

        {

            get { return _choices; }

            set { _choices = value; }

        }

        public void DisplayAccumulatedResult()

        {

            Console.WriteLine("The result will display in pie chart.");

        }

        public override string ToString()

        {

            StringBuilder sb = new StringBuilder();

            foreach (Choice answer in Choices)

            {

                sb.Append(String.Format("\n<radio>\t\t{0}",answer.Text));

            }

            return sb.ToString();

        }

    }

    public class SingleDropDown : IQuestion

    {

        public SingleDropDown(string t)

        {

            _text = t;

        }

        private string _text;

        private ArrayList _choices = new ArrayList();

        public string Text

        {

            get { return _text; }

            set { _text = value; }

        }

        public ArrayList Choices

        {

            get { return _choices; }

            set { _choices = value; }

        }

        public void DisplayAccumulatedResult()

        {

            Console.WriteLine("The result will display in bar graph.");

        }

        public override string ToString()

        {

            StringBuilder sb = new StringBuilder();

            foreach (Choice answer in Choices)

            {

                sb.Append(String.Format("\n<dropdownlist>\t\t{0}",answer.Text));

            }

            return sb.ToString();

        }

    }

    public class MultiCheckBox : IQuestion

    {

        public MultiCheckBox(string t)

        {

            _text = t;

        }

        private string _text;

        private ArrayList _choices = new ArrayList();

        public string Text

        {

            get { return _text; }

            set { _text = value; }

        }

        public ArrayList Choices

        {

            get { return _choices; }

            set { _choices = value; }

        }

        public void DisplayAccumulatedResult()

        {

            Console.WriteLine("It will show the two most common answer.");

        }

        public override string ToString()

        {

            StringBuilder sb = new StringBuilder();

            foreach (Choice answer in Choices)

            {

                sb.Append(String.Format("\n<checkbox>\t\t{0}",answer.Text));

            }

            return sb.ToString();

        }

    }

At last, here’s our static void main:

       static void Main(string[] args)

        {

            IQuestion q1 = new SingleDropDown("Do you like .NET?");

            q1.Choices.Add(new Choice("Yes", "1"));

            q1.Choices.Add(new Choice("No", "0"));

            IQuestion q2 = new SingleRadio("What is your favorite .NET language?");

            q2.Choices.Add(new Choice("C#", "1"));

            q2.Choices.Add(new Choice("VB.NET", "2"));

            q2.Choices.Add(new Choice("C++", "3"));

            q2.Choices.Add(new Choice("J#", "4"));

            IQuestion q3 = new MultiCheckBox("hehe?");

            q3.Choices.Add(new Choice("a", "1"));

            q3.Choices.Add(new Choice("b", "2"));

            q3.Choices.Add(new Choice("c", "3"));

            Survey survey1 = new Survey(".NET Survey");

            survey1.Question.Add(q1);

            survey1.Question.Add(q2);

            survey1.Question.Add(q3);

            Console.WriteLine(survey1);

            Console.ReadLine();

        }

  

So, no matter what the question type is, we just override the interface, and then do some implementation of it. This make our codes look more elegant and easier to manage.

  

Late and Early Binding

  

In our example above, we define the question type at the compile time (before the runtime). This is what we call early binding. Late binding is when we only know it at the runtime. Sometimes, we don’t know that what type is because maybe it stores in the database, XML files, or maybe user inputs. Take a look at this code block:

        static IQuestion GetQuestionType(int type, string text)

        {

            switch (type)

            {

                case 1:

                    return new SingleDropDown(text); break;

                case 2:

                    return new SingleRadio(text); break;

                case 3:

                    return new MultiCheckBox(text); break;

            }

        }

  

Now, the question type depends on the user input (at the runtime). Here’s the static void main:

  

        static void Main(string[] args)

        {

            Console.WriteLine("Please enter type of question:");

            Console.WriteLine(" - 0 for single drop down.");

            Console.WriteLine(" - 1 for single radio button.");

            Console.WriteLine(" - 2 for multiple check box.");

            int questionType = Int32.Parse(Console.ReadLine());

            IQuestion q1 =

GetQuestionType(questionType, "Did you like the program?");

            q1.Choices.Add(new Choice("Yes", "1"));

            q1.Choices.Add(new Choice("No", "0"));

            Survey survey1 = new Survey(".NET Survey");

            survey1.Question.Add(q1);

            Console.WriteLine(survey1);

            Console.ReadLine();

        }

Share this post: | | | |
Posted: Mar 26 2007, 09:20 AM by very_wel | with 3 comment(s)
Filed under: ,
Interface And Abstract Class

There are a lot of confusions, misconception, and misuses about Interface and Abstract Class. Actually the objectives of them are doing polymorphism.

Interface

Interface is a type perspective like class or structure. You cannot instantiate an object from an interface, but you could inherit it. Interface doesn’t contain any definition.

Interface has contract saying that “For those who want to inherit me, you have to implement all of my members (properties, methods, events, etc)”.

So if you to inherit from an interface, you would have contract with it saying, “Hei Interface, I will implement all your members”.

The example goes here:

I have an IAnimal interface. It consists 3 general methods that all animals would have for sure: Talk, Eat, and Sleep. So the code goes here:

    interface IAnimal

    {

        string Name

        {

            set;

            get;

        }

        string Talk();

        void Eat(string food);

        void Sleep();

    }

Then, I create a Dog class, inherit from IAnimal because Dog is an animal. It implements all of the methods.

    class Dog : IAnimal

    {

        private string _name;

        public string Name

        {

            get { return _name; }

            set { _name = value; }

        }

        public string Talk()

        {

            return "Bark";

        }

        public void Eat(string food)

        {

            Console.WriteLine("I just ate some " + food);

        }

        public void Sleep()

        {

            Console.WriteLine("I sleep...");

        }

    }

Notice that, if I remove any of the members of Dog class, the compiler will complains saying that “Hoi!!! You have to implements all of members that your interface has”

C# doesn’t allow multiple inheritances from two classes, but you can do that with interface.

Assume that we also have an IBird interface like this:

    interface IBird

    {