Luckily, Object Oriented Programming methodology gives us two ways: Containment and Inheritance. It's an age old adage that any software engineering problem can be solved by adding a layer of abstraction — this is — in essence, what Containment does. Inheritance works a bit differently, giving development a possibility of inheriting the "knowledge" of the original class and add new functionality. Additionally, C# 3.0 gives yet another option called Extension Methods.
To summarize, here are the three ways of functional class extension when applying changes to original class is not possible:
- Containment (OOP concept)
- Inheritance (OOP concept)
- Extension Methods (C# 3.0 concept)
Using the Code
In this example, we use aCar
class as the original
class to which we wish to add functionality. It already has the
functionality for accelarating and now we want to add the functionality
fo breaking. It's quite an important feature in a car, isn't it? The attached code sample is a console project in a Visual Studio 2008 solution. The application logic basically just creates a
Car
object and extends upon it with the above mentioned techniques. It accelerates and brakes that Car
objets(s) and shows trace information (status of Velocity property) on the screen. Here is Car class in UML:
Here is the
Car
class in code: public class Car
{
public string Name { get; set; }
public int Velocity { get; set; }
public Car(string name)
{
this.Name = name;
Console.WriteLine("\nCreated a {0}", name);
Console.WriteLine("Velocity is {0}", this.Velocity);
}
public void Accelerate()
{
this.Velocity++;
Console.WriteLine("Accelerated {0} to velocity {1}",
this.Name, this.Velocity);
}
}
So, the Car
class contains two properties, a constructor and an method for acceleration. Car
class is used like this:
As indicated in the comment, this Car
object don't know how to break. It's important to note that in the following examples, the original
Car
class stays untouched! Let's add braking functionality, or a
Break()
method, with containment:
CarContainer
class in code: public class CarContainer
{
public Car car;
public CarContainer(Car c)
{
this.car = c;
Console.WriteLine("\nCreated container for {0}", this.car.Name);
}
public void Brake()
{
this.car.Velocity--;
Console.WriteLine("Braked {0} to velocity {1} (with containment)",
this.car.Name, this.car.Velocity);
}
}
CarContainer
class is used like this: Next, let's add braking functionality with inheritance:
ChildCar
class in code: public class ChildCar : Car
{
public ChildCar(string name)
: base(name)
{
}
public void Brake()
{
this.Velocity--;
Console.WriteLine("Braked {0} to velocity {1} (with child class)",
this.Name, this.Velocity);
}
}
ChildCar
class is used like this:
Hide Copy Code
// Let's create a child object that inerits Car's ability to accelerate
// and adds functionality to break.
ChildCar c2 = new ChildCar("Volvo");
c2.Accelerate();
c2.Brake(); // This car knows how to break too.Next, let's add braking functionality with new C# 3.0 feature called Extension Method:
I have to admit that I'm not actually sure how to visualize an extension method, so please be kind to me if the above diagram does not comply to UML standards.
CarExtender
class in code: public static class CarExtender
{
public static void BrakeForHeavensSake(this Car c)
{
c.Velocity--;
Console.WriteLine("Braked {0} to velocity {1} (with extension method)",
c.Name, c.Velocity);
}
}
CarExtender
class is used like this: // Let's create a third car, this is a normal car.
// There has been defined an extension method to a Car object in the namespace,
// so the BreakForHeavensSake() method is "glued" to this object.
Car c3 = new Car("Mercedes-Benz");
c3.Accelerate();
c3.BrakeForHeavensSake(); // Works ok!
No comments:
Post a Comment