Switchers revisited in C#
2008-05-22 | Filed Under Software development |
For reasons that I will never understand, Microsoft’s C# does not support anonymous classes. So when applying the Switcher pattern in C#, we need to take a different approach to implementation.
A word of warning: this posting discusses the Switcher pattern that I introduced in an earlier posting. If you have not read this earlier posting, I would suggest doing so before proceeding, as it provides some fairly fundamental exposition.
Again, let’s start with some simple classes that require class-dependent processing. We’ll use fruit instead of animals this time, so nobody accidentally tries to mix Java code from previous posting with C# code from this one.
public abstract class Fruit { }
public class Banana : Fruit { }
public class Plum : Fruit { }
Here is the version of the client code that’s practically begging to malfunction once a new subclass of Fruit is introduced:
Fruit F = getRandomFruit();
if (F is Banana) {
Banana B = (Banana)F;
B.peel();
B.eat();
} else if (F is Plum) {
Plum P = (Plum)F;
P.eat();
spitOut(P.stone);
}
In C#, I implement the Switcher pattern using delegate methods (which are basically type-safe function pointers) that are passed as arguments to a Switcher method in an abstract Switcher class:
public abstract class FruitSwitcher {
public delegate void BananaMethod (Banana B);
public delegate void PlumMethod (Plum P);
public static void switchAll(
BananaMethod onBanana,
PlumMethod onPlum,
Fruit F)
{
if (F is Banana) {
onBanana((Banana)F);
} else if (F is Plum) {
onPlum((Plum)F);
}
}
}
Client code can then use the Switcher class thusly:
Fruit F = getRandomFruit();
FruitSwitcher.switchAll(
delegate(Banana B) {
B.peel();
B.eat();
},
delegate(Plum P) {
P.eat();
spitOut(P.stone);
},
F
);
This approach offers a number of features not included in the previous, Java-based Switcher class.
Firstly, the Switcher class offers class-switching logic as static methods rather than as constructors. This has the advantage that a single Switcher class can implement various collections:
public abstract class FruitSwitcher {
/* Delegate method definitions omitted */
public static void switchAll(
BananaMethod onBanana,
PlumMethod onPlum,
LemonMethod onLemon,
OrangeMethod onOrange,
Fruit F)
{ /* Implementation details omitted */ }
public static void switchCitrus(
LemonMethod onLemon,
OrangeMethod onOrange,
Fruit F)
{ /* Implementation details omitted */ }
}
Secondly, the delegate methods each include a parameter containing the original object, just re-cast to the respective class. This is for two reasons:
- It is highly likely that the class-specific client code will want to access the class-specific object members, so this would also be a good feature to add to any Java implementations.
- More importantly, this ensures that the delegate methods are specified in the correct order in the client code; if all delegate methods used a common
FruitMethoddelegate, it would only be a matter of time until someone put the arguments in the wrong order and thereby ended up trying to spit out a banana’s stone.
Other features are of course also possible, including code for dealing with null values (which could either throw an exception, or call a delegate onNull method). There is also no rule that states the Switcher method has to have the void return type; for example, we could implement FruitSwitcher.switchAll(...) so that the client code’s delegate method returns the color of the fruit.
Post Linx
Permalink | Trackback |
|
Print This Page |
Comments
Leave a Reply