Enums vs. Adapters
I like Enums and use them frequently for options and behavior. To an extent I use Enum's to control behavior. For example
enum Kind {
Kind1,
Kind2,
Kind3
}
class Example {
private Kind m_kind;
public int SomeAction() {
switch (m_kind1) {
case Kind.Kind1:
return ActionForKind1();
case Kind.Kind2:
return ActionForKind2();
case Kind.Kind3:
return ActionForKind3();
default:
throw new InvalidOperationException("Invalid Kind");
}
}
}
This is an acceptable pattern and use for enums. However if you take a step back, what I've actually done here is use an enum to implement an adapter pattern. I've just been a bit lazy about it and not actually coded up the classes.
To an extent though this violates the principle of single use as Example now performs N different behaviors based upon the enum. But lets face it, if ActionForKindN() is just a simple 2 line function then is it really worth it to create and maintain an adapter pattern? A purest would likely say yes but I'm more pragmatic and don't believe so.
Once the functions reach a certain level of complexity though an adapter pattern is much more suitable. Over time I find that many of my similar patterns evolve to level.
Yet I struggle to define the point at which an adapter is suitable. After several recent experiences I started adapting the following rules. If any of them is violated then I switch from an enum based behavior to adapter based behavior.
- The Action* method contains state
- There are more than 2 functions which change their behavior based on the enum value
- All methods in the class change their behavior based on the enum
Anyone have a better set?
Comments
Anonymous
June 18, 2008
> A purest would likely say yes but I'm more pragmatic and don't believe so. I suspect that most of the so-called purests have long forgotten the goal of the design patterns is to reduce maintenance costs. It is like a biblical literalist who can recent any Bible passage word for word but has no idea what it means beyond "this really happened".Anonymous
June 18, 2008
This seems like it should just be implemented with polymorphism, unless I'm missing something: [code] abstract class Example { public int SomeAction(); } class ExampleKind1 extends Example { public int SomeAction() { //method body for ActionForKind1() } } class ExampleKind2 extends Example { public int SomeAction() { //method body for ActionForKind2() } } class ExampleKind3 extends Example { public int SomeAction() { //method body for ActionForKind3() } } [/code]Anonymous
June 18, 2008
This has prompted me to finally post something I've had in mind for a while, about behaviour switching using public delegate properties: http://blogs.windowsclient.net/rendle/archive/2008/06/19/more-thoughts-on-enums-vs-adapters.aspxAnonymous
June 19, 2008
Actually, I think you are using the strategy pattern. I usually select this pattern whenever the algorithm is likely to change, or get extended. For example, when new enums are likely to get added in the future. In that case it is definitely appropriate with strategy instead of enums. Big switch logic will tell you when you need this pattern.Anonymous
June 19, 2008
Beginer question: Why this is an acceptable pattern and use for enums as you mentioned?Anonymous
June 20, 2008
@Livdro, I think it's acceptable as long as it 1) is valuable for your code and 2) doesn't hit the rules I described above. Even if #2 comes into play I still occasionally keep the pattern if I don't think the solution will get in more complex as time goes on.Anonymous
June 20, 2008
If that is the only place you use Kind, redefine it as a delegate. Then where you would say m_kind = Kind1; instead write m_kind = ActionForKind1; This makes SomeAction simply: return (m_kind)();Anonymous
June 20, 2008
What you wrote has NOTHING to do with an Adapter pattern at all. A poor strategy maybe, but definitely not an Adapter.Anonymous
June 20, 2008
@Jason, Based strictly on this example you're right. I was attempting to be a bit more general though. Usually I find that when I hit this type of problem I'm using a variety of other objects and state to implement these actions. In this case it is an adapter pattern being implemented.