One Approach to OOP Design
There are a number of patterns that show up in architecture and design and I have a set of favorites that I pull out for certain projects. In the past
I've written about indirection but this time I want to talk about a pattern I use for a system. I'm sure someone's thought up a formal name for it, but I'll be honest - there's a gap in my education in terms of named design patterns that I'm in the process of filling.
I use something that's similar to the Template pattern. This pattern is a way of defining an abstract generic class that exposes functionality, but provides as much flexibility for the implementor of the concrete functionality as possible. The pattern uses two paths depending on the design needs:
- Designer must retain control over an entire process
- Designer wants to offer flexibility of design
Here's how I do the both of these. This is a chunk of code that models a bass guitar. There's a lot more that can go in here - to keep it short, I just put in the way that you would restring the instrument.
Now, as a person with an understanding of the forces on the neck of a bass, I know that you never want to remove all the strings in one go. Instead, you want to replace them one at a time, so I've made a method for doing that which is sealed that does them one at a time, calling replace string.
public abstract class MyBass {
public sealed void Restring() {
foreach (BassString bassString in _strings) {
ReplaceString(bassString);
}
}
public virtual void ReplaceString(BassString bassString) {
BassString newString = LLGetNewString(bassString.Kind);
SwapStrings(bassString, newString);
LLDestroyString(bassString);
}
protected void SwapStrings(BassString oldString, BassString newString) {
int i = _strings.IndexOf(oldString);
if (i < 0) {
throw new Exception("Internal error - string not found");
}
_strings[i] = newString;
}
protected abstract BassString LLGetNewString(BassStringKind kind);
protected abstract void LLDestroyString(BassString string);
}In this code, I've done most of the work for you that handles everything necessary to restring the instrument. The core of this is ReplaceString, which is a public virtual method. It does everything in pieces that are accessible to a subclass. In other words, if you didn't like my implementation, you could override it, but use the same pieces that I used in a different order. In the case of Restring(), I'm protecting you from damaging the bass - that would be bad, but I do let you rework replacing a single string. Maybe you want to fire events or purchase new strings if needed, etc.
Finally, I know that I need to make strings and throw them away. In this case, I could have defined a BassStringFactory class, but I wanted to show another technique. I don't know anything about making a string or destroying it, so I just defer that to an abstract method that my client needs to tend to. Why LL? LowLevel. I typically use this naming convention in code that's used to separate out high level, generic code from low-level OS/platform dependent code.