Library tutorials & articles
A Twisted Look at Object Oriented Programming in C#
Where is My Inheritance?
In this chapter you will learn how the fictional character Phaedrus explains the complex subject of a class hierarchy in "Zen and the Art of Motorcycle Maintenance", Robert M. Pirsig, Quill, 1974, 418 pp. You will also learn the art of "black box programming" with abstract classes and abstract methods.
Lets Get Abstract
One of the major advantages of OOP is that is simplifies the task of creating and maintaining complex applications. The road to this simple life is through "abstraction". Abstraction is the ability to "decompose" a complex problem or structure into simpler problems or pieces. In abstraction, you try to find the "essence" of a thing or idea. You break down a complex problem into smaller, more manageable chunks. Abstraction is what the character Phaedrus describes in Chapter 8 in "Zen and the Art of Motorcycle Maintenance."
"John looks at the motorcycle and he sees steel in various shapes and has negative feelings about theses steel shapes and turns off the whole thing. I look at the shapes of the steel now and I see ideas. He thinks I am working on parts. I'm working on concepts."
Phaedrus then proceeds to divide up the concept of a motorcycle into components and functions. Then he divides the components into a power assembly and a running assembly. To which he concludes:
"Finally you see that while I was splitting the cycle up into finer and finer pieces, I was also building a structure. This structure of concepts is formally called a hierarchy and since ancient times has been a basic structure for all Western knowledge.... That's all the motorcycle is, a system of concepts worked out in steel."
Thus the fine art of motorcycle maintenance is based on abstraction, decomposing the complex concept of the motorcycle into a hierarchy of smaller simpler concepts. The fine art of OOP is also based on abstraction. You decompose the complex application domain into a hierarchy of smaller pieces that make the application easier to create and maintain. Now I am not saying that Phaedrus was a great object oriented programmer, but he clearly understood the importance of critically examining the problem at hand and dividing up the problem into a hierarchy of ideas.
Let's look at Phaedrus' motorcycle in C#. First, a motorcycle is a vehicle since it can carry a payload. Second, it is motorized since it has a motor and consumes fuel. The "essence" of a motorcycle is that of a "vehicle" that is "motorized". Of course a motorcycle differs from a a car, an airplane, or a boat. Motorized vehicles can be ridden, driven, flown or piloted. Land vehicles can have two wheels or four wheels. What we have here is a hierarchy of ideas. We go from a most general concept to a more specific or "specialized" concept. Just as Phaedrus' abstraction of the motorcycle made him a better mechanic, your abstraction of a complex application, makes you a better and more efficient programmer. You do not think of a program as concrete parts (global data and methods). You think of a program as a hierarchy of ideas. Welcome to "Zen and the Art of Application Maintenance."
Inheritance Is the Means to Abstraction
Let's face it. Just creating a hierarchy of ideas is not enough. The language must provide a mechanism to support the abstraction hierarchy. That mechanism is called inheritance. Inheritance allows programmers to modify or extend (or inherit) the properties and behavior (methods) of a class. It promotes "code reuse", the holy grail of programming. Throughout the .NET framework, you see the use of abstraction and class hierarchies.
Inheritance in the .NET Framework
All classes in the .NET framework derive from a single base class called Object. The Object class is the "root of the type hierarchy." Even the built in value types are aliases to classes that derive from Object, so that int actually maps to System.Int32, which derives from Object. As a result, you can call the Object class methods Equals, Finalize, GetHashCode and ToString on any class in the .NET framework. This makes it very easy to debug an application using console output since any .NET class will implement the ToString() method.
An example of inheritance in the ASP.NET framework is a TextBox. Here is the class hierarchy for System.Web.UI.WebControls.TextBox:
System.Object
System.Web.UI.Control
System.Web.UI.WebControls.WebControl
System.Web.UI.WebControls.TextBox
The TextBox control inherits from WebControl which inherits from Control which inherits from Object. Wow. And I thought my family tree was complicated.
Getting Your Inheritance
In the following section you, a twisted programmer, build a "SpaceToaster" class in C# that inherits from the Toaster class.
Disclaimer: This is a very twisted tutorial. Reading this online book may be hazardous to your mental health.
You have been contracted by a famous "Boy Genius" movie cartoon character to extend the common household toaster for use as a orbiting satellite transmitter. SpaceToaster is a specialization of a Toaster that extends the behavior of the lowly toaster into a satellite.
Note: For those of you who don't know what cartoon movie I am talking about, either you have no children or you need to get out more. Here's a hint: "Jimmy Neutron Boy Genius", Paramount Movies, Nickelodeon Movies, 2001.
First you need to delete the Main method of the Toaster3 class since an executable can only have one entry point. You can now create a new class "SpaceToaster" that inherits all of the code in the Toaster3 class and has a Main method:
class SpaceToaster : Toaster3
{
[STAThread]
static void Main(string[] args)
{
}
}
This is not a very useful class! The syntax for inheritance is
class ChildClass : ParentClass {}
Note: Toaster3 is the parent class and SpaceToaster is the child class. You could also say the the child class is a "subclass" or "derived class" and that the parent class is the "superclass" or "base class." The child inherits the knowledge (methods and variables) of the parent class, but can only "touch" the public (and protected) methods and variables of the parent. Sort of reminds me of a trust account. The child's ability to "touch" the assets of the trust is limited.
Next, you can add a new behavior to the derived class extending the functionality of the lowly Toaster:
class SpaceToaster : Toaster3
{
[STAThread]
static void Main(string[] args)
{
SpaceToaster st= new SpaceToaster();
Console.WriteLine(st.Broadcast());
Console.ReadLine();
}
public string Broadcast() // <-- new behavior
{
return "Hello From Earth!";
}
}
Which amazingly outputs: "Hello From Earth!"
Since SpaceToaster also inherits the methods and properties of Toaster3, it can still make toast while orbiting planet Earth:
class SpaceToaster : Toaster3
{
[STAThread]
static void Main(string[] args)
{
SpaceToaster st= new SpaceToaster();
Console.WriteLine(st.Broadcast());
st.SecondsToToast= 10;
Console.WriteLine(st.MakeToast());
Console.ReadLine();
}
public string Broadcast()
{
return "Hello From Earth!";
}
}
Inheritance vs. Containment
One difficult design decision is to decide if a class should inherit from a parent class or hold a reference to a new object. Inheritance represents an IS_A relationship from a generalization to a specialization. Containment represents a HAS_A relationship between the whole and a part. So a car IS_A motorized vehicle, but HAS_A radio. The two relationships can be expressed in code thusly:
class Radio
{
...
}
class Vehicle
{
...
}
class Car : Vehicle
{
Radio r= new Radio();
}
The essence of a car is that it is a vehicle. A radio is not essential to a car. Instead a radio is part of a car. That said; let me acknowledge that none of my teenage children agree with me. They are adamant that a radio with a CD player _is_ essential to a car. So a teenage design looks more like:
class TeenageVehicle : Radio
{
...
}
Public, Protected and Private
Heck. I forgot that I had promised in Chapter 1 to explain the access modifier "protected". In a nutshell, a private method or variable is only touchable within the class that declared the variable. A public method or variable is touchable from outside the class. A protected method or variable lies between public and private. Protected methods or variables are only touchable (visible) from within the declaring class _or_ from a class that inherits from the declaring class. So declaring a variable protected allows a designer to access your base class variable inside of their derived class. Suffice it to say that there has been a lot of discussion about the use of private versus protected in class design.
Make It Abstract
Is your head spinning yet? If so, I apologize, but I need to introduce one more subject that is related to abstraction, the "abstract class". An abstract class defines zero or more abstract methods. An abstract method defines the signature of the method, but not the body of the method. You cannot create an instance of an abstract class.
Note: Similar in concept to an abstract class is the concept of an "interface". However, an interface contains no implementation, only abstract methods, and is similar to a pure virtual class in C++.
Nothing helps explain a new programming idiom like sample code. Here is an abstract C# class "SpaceToaster" that defines an empty abstract method "BroadcastID()":
abstract class SpaceToaster : Toaster3
{
public string Broadcast()
{
return "Hello From Earth!";
}
abstract public string BroadcastID(); // <-- No body or implementation!
}
If you try to create an instance of this abstract class, the compiler will complain:
Cannot create an instance of the abstract class or interface 'JAL.SpaceToaster'
An abstract class is useful in at least four distinct situations.
Defer the Implementation to Another More Knowledgeable Class
First, an abstract class is useful when you know what the class should do, but you don't know how to implement the total solution. In this idiom, you defer the actual implementation or body of one or more methods to another class. A good example of deferred implementation is a sorting class, say a quicksort. You may know how to do a quicksort, but you cannot know the ordinality or ranking of every possible object that needs to be sorted. You may want to write a complex generic sort routine and declare the class abstract with an abstract method Compare(object1, object2). You can even call this abstract Compare method in your Sort class (cool!). Now another programmer with a custom object that needs to be sorted, can inherit from your Sort class and implement the Compare method with code that defines the ordinality of the custom object, reusing your generic quicksort algorithm! In a sense, the child class inherits the incomplete implementation of your Sort class.
Note: C# supports single inheritance of implementation and multiple inheritance of interfaces. In C#, see the IComparable interface.
Formally Hide or Encapsulate the Implementation for Flexibility
Alternatively, you may just want to formally hide or encapsulate the actual implementation of a method. By requiring clients to program to the public view, the abstract method, rather then to the actual implementation, you promote information hiding or encapsulation. This allows you to change the implementation in a future release, without affecting any client calls.
The key concept here is that the implementation can change without affecting a call to your abstract method. This is yet another example of how OOP makes it easier to maintain your code. If you define an abstract method GetData(), the data could come from a database query. To the caller of the abstract method, GetData() is just a black box that returns data. If you later decide to change the concrete implementation of GetData() from a database solution to a text file solution, you can recompile the concrete implementation of GetData() without recompiling classes that call the abstract method GetData().
More interestingly, you could compile an updated class with a different name and load the updated class at runtime (perhaps passing the name of the new class in the Main method). As long as the updated class derives from the same abstract class and as long as the client makes calls through the abstract class, there is no need to recompile the client application. Cool!
Allow Runtime Variation in Implementation
This concept is difficult to grasp at first, but is an extremely powerful idiom. In this idiom, one or more concrete classes inherit from an abstract class, but each child class may provide a different implementation of the the abstract class method or methods. The client does not know what concrete class is going to be requested at runtime, but simply invokes the public view of the method. The actual runtime behavior will depend on which concrete classes is requested at runtime. Thus the method can have more than one form at runtime, which is referred to as polymorphism, to have more than one form.
The classic example is an abstract class Drawable with an abstract method DrawYourself(). Different concrete classes of Drawable items (such as circle or square) implement the DrawYourself() method in a unique manner and inherit from Drawable. You can now create a Hashtable of non-null Drawable objects and iterate over the collection calling someObjectInHashtable.DrawYourself(). Each concrete object in the collection is guaranteed to implement the DrawYourself() method.
Note: Polymorphism implies late binding of a method name to a concrete implementation. In some languages, this magic is done with a v_table, which stores an offset pointer to the actual concrete implementation. OK. That was probably more than you wanted to know.
Mark Thyself
Finally, the abstract class can simply be a marker, allowing you to identify all classes that are based on your empty abstract class like this:
abstract class MyMarker {} // <-- no abstract methods
class Child : MyMarker
{
static void Main(string[] args)
{
Child c= new Child();
if (c is MyMarker)
{
System.Console.WriteLine("Is mine.");
}
System.Console.ReadLine();
}
}
Outputs: "Is mine."
Sample C# Code
We finish this chapter with a bit of sample code that demonstrates the syntax of inheritance from an abstract class. Here again is the abstract version of the SpaceToaster class:
abstract class SpaceToaster : Toaster3
{
public string Broadcast()
{
return "Hello From Earth!";
}
abstract public string BroadcastID(); // <-- No body or implementation!
}
This abstract class defines the public outside view or signature of the method "BroadcastID". The method is declared with a return type "string" and an empty parameter list. The declaration says nothing of how the method is implemented.
Any concrete class that inherits from this abstract class, must provide a body or implementation for the function BroadcastID(). Here is an concrete version of a class that inherits from the SpaceToaster class and provides an implementation of the abstract method BroadcastID(). Note the use of the keyword "override" which tells the compiler that you are explicitly implementing the abstract method BroadcastID in your concrete class.
class TwistedSpaceToaster : SpaceToaster
{
[STAThread]
static void Main(string[] args)
{
TwistedSpaceToaster tst= new TwistedSpaceToaster();
Console.WriteLine(tst.BroadcastID());
Console.ReadLine();
}
public override string BroadcastID() // <-- concrete implementation
{
return "Manufactured By Twisted.";
}
}
Running this code outputs: "Manufactured By Twisted."
Congratulations at making it this far. All of this theory, however, is giving me a headache. Lets move on to some real world techniques for writing better programs. In the next twisted chapter, we learn a basic design pattern for writing reusable code, the Model-View/Controller pattern.
Related articles
Related discussion
-
C# video Editing/rendering
by pkuchaliya (0 replies)
-
How to Fill DataSet with more records (around 1 lakh) in a faster way
by Jayaram P (0 replies)
-
Can't print on the network with MSADESS ??
by anatha1 (2 replies)
-
Very Urgent regarding deleting the images from a folder
by Nanosteps (6 replies)
-
DataGridViewComboBoxColumn not showing values
by sachinkalse (0 replies)
Related podcasts
-
Object-Oriented Programming in Ruby
In this episode, I talk with Scott Bellware about object-oriented programming in Ruby, and Ruby's object model. This is taken from a private conversation, and the audio quality suffers at times. Much thanks to Scott for allowing this to be released.This episode of the Alt.NET Podcast is bro...
thanks for the useful info
regards
If you have tried developing large extensible business applications where data needs are in the gigabytes if not terabytes in any static OO based programming language, then you are in for a lot of headaches. OOP does not lend itself well to this type of development as so called real world objects do not translate well into OO objects, most so called real world objects in the business world translate to data. This fact is lost on many managers, developers and analysts. Most applications in the real world are business related and not some academic project. I have been privy to many failed projects because of the selection of high level development language invariably they have always been statically typed. The one overlooked programming paradigm that has been neglected because of the OO hype is table or data driven programming. This form lends itself well to business related applications and can be used in conjunction with any programming paradigm but work wonders with a dynamically type languages. It is easily extensible, scalable and can be easily modified without throwing hundreds of users of the network.
As for Interfaces, function overloading, generics, templates, reflection, RTTI, default parameters, adding scripting etc, these are kludges to make a static language do what a dynamic language already does, and done badly at that.
When I first learned OO programming I was primarily excited by code re-use on the object side of the equation. Objects considered as providers of services to other code that acts as a client.
After many years of developing OO application I eventually grew to see the vast web of code dependencies and to realize that every piece of client code is itself serving some other code i.e. tier 1 calls tier 2 calls tier 3 (tier 2 code is being served by tier 3 but is serving tier 1). I realized that my objects provided a great deal of code re-use but what about all the code that used those objects? That is where Interfaces come in. Thanks to an interface all of your code that uses object A can be swapped out to use object B, C, D, or E regardless of the object type and therefore ancestry. Regardless of object implementation.
In another sense, Interfaces are more "fair". If an object can perform the services declared in the interface then it can be used by code that uses the interface. This means no strict binding to particular object hierarchies and that can save a lot of headaches.
Read the article even if you work with OO, give it to people that are just starting OO.
I'd really like to see this article as the core of explaining more OO topics, in the same format.
Workaround:
Save printable page as complete web page.
Edit default.css file in _files subfolder of saved page.
Change the CODE.Pre line to:
CODE.pre {
PADDING-RIGHT: 7pt; PADDING-LEFT: 7pt; WIDTH: 100%; COLOR: #000000; PADDING-TOP: 7pt; BACKGROUND-COLOR: #fbedbb
}
(removes the display:block)
RRIOS:
Thanks for the excellent write up on this concept. It has actually helped quite a bit to give me a better perspective on the subject of Interfaces. I particularly liked your analogy of Royal family vs democracy.
Thanks!
Inheritance is rigid. It´s like being born into the royal family. Sure, you get alot of privileges without doing anything, but you carry all the baggage and stigmas that come with your family name. You can´t JOIN the royal family. You have to be born into it.
Interfaces are democratic. A system that declares an interface is saying "I dont care who you are or how big or small you are: if you agree to these rules you may join the club". Think of your drivers license. The DVM declares an interface and ANYONE can use the road system as long as they stick to the rules (implement the interface).
In programming terms, suppose you are using two systems, in one you have created a Vehicle class and have several child classes like Car, Bicycle, and Bus. In the other you have cooking items, say for a recipe system. You may have classes like Flour, Eggs, Carrot. These two groups of classes have nothing to do with each other.
You create a third point of sale system for a retail store, and you plan to sell among other things, bicycles and flour. Your point of sale system could declare an IRetail interface that defines to other classes what they need to implement in order to participate in the Point of Sales system like Price(), TaxKey() and UPC().
This means you can reuse your Bicycle and Flour classes, just by adding the implementation of the IRetail interface in order to use them in the Point of Sale system. This new system will be capable of handling any kind of object as long as it implements IRetail.
So Interfaces are an advantage to both the system that declares the interface (they dont care what class of object they are handling) and to the classes that implement them (they can participate in any system as long as they agree to the rules).
http://www.jroller.com/page/haruki_zaemon/20031201#adapter_classes
An interface allows a developer, but more specifically a designer, to say "These features must be implemented." It is then up to the developer to work out the details of how to deliver on those features/abilities. It creates a boundary of expectation from other users of the object.
They can say "Oh I i see it implements IInterface, therefore it will definitely have X, Y and Z". It is quite useful when you wnat to implement concepts in multiple objects instead of concrete functionality. For instance, say you had two types of collections one was a tree and one was a jagged array of parent/child IDs (which in many ways is a tree represented another way but they are syntactically different). If you wanted to implement something like "GetParent", you couldn't (well maybe you could, but go with me for the sake of example) implement one generic function for "GetParent" that would work for both the Jagged Array and the Tree. YOu could however have both objects implement an interface, and then have the developer do the syntactic details of returning an items parent.
Anyway, more half baked analogies.
One of the questions that still persists for me, (and something I have never seen a discussion on) is what real benefit Interfaces offer; it seems they offer some, but that it is somewhat limited. Let me give an analogy, in the form of an old joke. An engineer, a phycicist and an economist were on a desert island with a can of pineapple that had survived the boat wreck. Both the engineer and the physicist made multiple attempts using their scientific knowledge to open the can of pineapple, but to no avail. After some time the economist remarked: "This shouldn't be a difficult problem to solve." "Well, tell us", replied the engineer and physicist in unison. Said the economist, " Assume a can opener."
This is what economists do. They make assumptions and think they have contributed something. Is this not like Interfaces, whose job, it appears, is to make policy statements without ever getting around to implementing anything? All the same amount of coding has to be done whether the interface is used or not; in fact there may be considerable duplication of code if minor changes to an implementation are needed. So what has been achieved? Not nearly as much, it seems, as inheritance would offer, were full inheritance of pure methods possible. This is perhaps why an Abstract class seems to be more useful.
I would appreciate anyone who can resolve my perplexity here. I could learn something.
Pretty good article. Its the first time i acutally laughed and smiled while reading your article. Very good read..
There are a few inaccuracies though just to point out...
1.
MyClass c;
c.someMethod()
does not throw a null reference exception. It actually throws a compiletime error "Use of unassigned local variable". In c# variables much be assigned before use or something like that..
2.
array[] = new array[10]
only returns an array of null references if it you are declaring an array of ref type. If it is a value type, the values will be assigned their default values..
and 3.
comparing type compatibility in most cases should use "as" instead of "is" because if you use is before casting the type, you in effect check the type twice, once before the cast, and once during the cast..
Its just an optimisation really..
Other than that, like i said before, best read i have ever had. Learned a few new words too like "gumption". Who would have thought..
This intro to C-Sharp is a very good tutorial, but its only an intro. The naming of variables and methods need to be standardized for easier readability. It also does not cover data driven dynamic web apps.
...it is a good article and Microsoft does advocate clearer naming of Object variables.
It's a shame you never LEARNED proper grammer.
This thread is for discussions of A Twisted Look at Object Oriented Programming in C#.