Recently I was working on some articles for our new Refactoring Section. In the process I wanted to be able to explain why a 'bad' habit is in fact bad. After all, what value is refactoring something if it's not an improvement. In some instances things are black and white, some things just plain old stink. In others it's a matter of ok versus better or better versus best. I was working on the Extract Interface refactoring and I got to thinking about Polymorphism. I remember a while back when a former company of mine was interviewing VB programmers and one of the interview questions was "How do you implement Polymorphism in VB" (this was VB6. The guy made three totally unrelated classes, and provided a method for each one called DoSomething. He said that this was really limited but that VB wasn't his strong point, as far as he knew it didn't support inheritance and that if we wanted to see how to correctly implement it, let him write it in C++ or Java. That he did and eventually he got the job.
Well both VB.NET and C# Support inheritance and by extension, REAL Polymorphism. If you look at the defintion I provided, an integral part of the definition is:
"More specifically, it is the ability to redefine methods for derived classes. "
The emphasis added was by the author, not me. Anyway, the key phrase here is Derived Classes! So, if you don't have derived classes you don't have real polymorphism! Anyway, let's look at an example of the wrong way to implement polymorphism in C#:
First, I'm going to define a class called Computer, from that I'll derive two classes, DataBaseServer and WebServer. They'll each have a method called ShowDetails . (Before I go on, I'll admit I got a little lazy. I have multiple overloaded constructors in the base class and I only implemented one constructor in the derived class. In practice I'd have a matching constructor in each of the derived classes for each constructor in the base class). I also created a form with Two Buttons, BadTest and GoodTest. I create an Array of Computer Objects, and add an instance of each derived class to the array. From there, I call a function named ShowDetails (I wasn't really original tonight in my naming conventions, but this showDetails has no relation to the class' showDetails..it just iterates through the array and outputs the results:
The Classes:
| using System; using System.Diagnostics; namespace KnowDotNet.VirtualMethods { public enum ComputerType{Desktop, Laptop, Server, PDA}; /// <summary> /// Summary description for Computer. /// public class Computer { private string brand; private ComputerType computerStyle; public Computer(string brand) {//Be a good OOP programmer and use the Accessor this.Brand = brand; } public Computer(string brand, ComputerType computerStyle) { this.Brand = brand; this.ComputerStyle = computerStyle; } public string Brand { get{return brand;} set{brand = value;} } public ComputerType ComputerStyle { get{return computerStyle;} set{computerStyle = value;} } public void ShowDetails() { Debug.WriteLine("Computer Brand: " + this.Brand); Debug.WriteLine("Computer Style: " + this.ComputerStyle.ToString()); } } class DataBaseServer : Computer { public DataBaseServer(string brand) : base(brand) { } public DataBaseServer(string brand, ComputerType computerStyle) : base(brand, computerStyle){} public new void ShowDetails() { Debug.WriteLine("DataBaseServer Brand: " + this.Brand); Debug.WriteLine("DataBaseServer Style: " + this.ComputerStyle.ToString()); } } class WebServer: Computer { public WebServer(string brand) : base(brand){} public WebServer(string brand, ComputerType computerStyle) : base(brand, computerStyle){} public new void ShowDetails() { Debug.WriteLine("Web Server: " + this.Brand); Debug.WriteLine("Web Server: " + this.ComputerStyle.ToString()); } } } |
| Computer[] ComputerInfo = new Computer[3]; private void cmdBadTest_Click(object sender, System.EventArgs e) { ComputerInfo[0] = new DataBaseServer("Dell",ComputerType.Server); ComputerInfo[1] = new WebServer("Hewlett-Packard", ComputerType.Server); ComputerInfo[2] = new Computer("Compaq", ComputerType.Desktop); ShowDetails(); } public void ShowDetails() { for(int x =0; x < ComputerInfo.GetLength(0); x++)<BR> { ComputerInfo[x].ShowDetails(); } } |
| Computer Brand: Dell Computer Style: Server Computer Brand: Hewlett-Packard Computer Style: Server Computer Brand: Compaq Computer Style: Desktop |
| using System; using System.Diagnostics; namespace KnowDotNet.VirtualMethods { public class ComputerGood { private string brand; private ComputerType computerStyle; public ComputerGood(string brand) { //Be a good OOP programmer and use the Accessor this.Brand = brand; } public ComputerGood(string brand, ComputerType computerStyle) { this.Brand = brand; this.ComputerStyle = computerStyle; } public string Brand { get{return brand;} set{brand = value;} } public ComputerType ComputerStyle { get{return computerStyle;} set{computerStyle = value;} } public virtual void ShowDetails() { Debug.WriteLine("Computer Brand: " + this.Brand); Debug.WriteLine("Computer Style: " + this.ComputerStyle.ToString()); } } class DataBaseServerGood : ComputerGood { public DataBaseServerGood(string brand) : base(brand) { } public DataBaseServerGood(string brand, ComputerType computerStyle) : base(brand, computerStyle){} public override void ShowDetails() { Debug.WriteLine("DataBaseServer Brand: " + this.Brand); Debug.WriteLine("DataBaseServer Style: " + this.ComputerStyle.ToString()); } } class WebServerGood: ComputerGood { public WebServerGood(string brand) : base(brand){} public WebServerGood(string brand, ComputerType computerStyle) : base(brand, computerStyle){} public override void ShowDetails() { Debug.WriteLine("Web Server: " + this.Brand); Debug.WriteLine("Web Server: " + this.ComputerStyle.ToString()); } } } |
| ComputerGood[] ComputerInfoGood = new ComputerGood[3]; private void btnGoodTest_Click(object sender, System.EventArgs e) { ComputerInfoGood[0] = new DataBaseServerGood("Dell",ComputerType.Server); ComputerInfoGood[1] = new WebServerGood("Hewlett-Packard", ComputerType.Server); ComputerInfoGood[2] = new ComputerGood("Compaq", ComputerType.Desktop); ShowDetailsGood(); } public void ShowDetailsGood() { for(int x= 0; x < ComputerInfoGood.GetLength(0); x++)<BR> { ComputerInfoGood[x].ShowDetails(); } } |
| DataBaseServer Brand: Dell DataBaseServer Style: Server Web Server: Hewlett-Packard Web Server: Server Computer Brand: Compaq Computer Style: Desktop |