Tuesday, October 04, 2005

Implementation and Inheritance Primer

* Note: This post is geared toward beginners, and should be relatively easy to understand. (Note 2: forgive the lousey code formatting -- I'll figure something out for that)

Lacking a more in-depth topic, I thought I’d take a few moments today to share a great way to explain the difference between inheritance, and implementation inheritance (that is, implementing or inheriting an Interface).

To put it as a one-liner, inheritance is like saying “is a…”, whereas implementation inheritance is like saying “acts like…”.

Consider the following C# code example. In the example, we have an abstract class named “Dog”. (Recall that an abstract class cannot be instantiated – it may only be inherited)

public abstract class Dog : IDog

{

private string _name;

public Dog()

{

//Standard object constructor

}

public void Bark(int times)

{

//Actual code here to make the dog bark

}

public void Sit()

{

//Actual code here to make the dog sit

}

public string Name

{

get

{

return _name;

}

set

{

_name = value;

}

}

}

The Dog object presumably encapsulates all of the basic required functionality of a dog. We have a Name property and methods for Bark and Sit. Any object that class that inherits Dog will have access to all that functionality.

We also have an Interface, aptly named IDog. (Note to newbies: best practices are that Interface names are always prefixed with the letter I)

public interface IDog

{

void Bark(int times);

void Sit();

string Name{get; set;}

}

In contrast to an abstract class, an interface does not contain any functional source code. Rather, it merely defines the way in which objects that implement it may be interacted with. In our example, any classes that implement the IDog interface must have their own Name property and Bark and Sit methods.

The following are examples of implementing the IDog interface, and inheriting the Dog class:

public class Poodle : Dog

{

public Poodle()

{

//Because this inherited the actual dog object,

//we can make the base class do the bark

base.Bark(3);

}

}

public class Collie : IDog

{

public Collie()

{

//standard constructor, but we don't have

//any functionality available from IDog -

//it simply defines what methods and properties we

//must have

}

public void Bark(int times)

{

//required by IDog interface

throw new NotImplementedException();

}

public void Sit()

{

//required by IDog interface

throw new NotImplementedException();

}

public string Name

{

//required by IDog interface

get

{

throw new NotImplementedException();

}

set

{

throw new NotImplementedException();

}

}

}

Note that in our example, the Dog class itself implements the IDog interface. Strictly speaking, it doesn’t have to. However, if this were real world code, we could use the IDog interface as one way to address the Class Factory pattern as in the following example:

public class DogFactory

{

public DogFactory()

{

}

public IDog GetDog(int height)

{

if (height <>

{

return new Poodle();

}

else

{

return new Collie();

}

}

}

In the real world, this approach is most often seen in functionality that interacts with databases. Taking this approach allows the programmer to write code that will interact with logic that is highly optimized for a specific database platform, while still affording the ability to support multiple platforms. I’ll discuss more about this topic in a future post.

No comments: