After watching: The Clean Code Talks -- Inheritance, Polymorphism, & Testing
I checked my code and noticed a few switch statements can be refactored into polymorphis
First of all, Java actually has great enums that can be used polymorphically. I'm no Java programmer so someone else can certainly give a good example (I can't). Also, consider that polymorphism is often just overkill. I've also just seen the talk, and it's great, but it only provides guidelines, no silver bullet.
It's especially not true that all switch
statements can be replaced. State machines is actually one case where enums make a lot of sense. I use state machines a lot in parsing. Sure, this can be done with a design pattern and class polymorphism. But it's much (much) more code, it performs the same job, only slower, it's not a jot more readable and it's a solution that's only needed in one single place, without any code recycling. Using subclassing here simply has no advantage.
Then again, this is an exception. In general, subclassing is often the better solution in languages that support it well.
EDIT: I notice that this might raise controversy. Of course there are a lot of good solutions that encapsulate parsing, from regular expressions to parser generator frameworks such as Antlr. Nothing wrong with them and in all but the trivial cases these are the better solution. However, I work a lot with low-level code (not in Java, obviously), where regular expressions aren't supported and parser generators also incur an overhead.
"Does this mean enums are "evil" in OO-design and should be eliminated with polymorphism?"
Usually.
switch/enum constructs can be any of a number of polymorphic structures: State and Strategy are two common ones that come up most often.
I hesitate to call anything evil. It's a question of "How heavy do you want to engineer this".
Enums/switches are fine - in some areas. Setting up a class hierarchy is overhead that isn't always needed for the problem. But the more code in the case statement, the more probable it is that, yeah, maybe you should be moving towards a heavier approach.
My classic experience is a compiler I wrote for a class a few years ago. My roommate had the same class with me, and we approached it in two very different ways. I took a heavy-OO approach, full of polymophism. He took a heavy-C approach, using enums and unions. His code was ~1/2 the size of mine LOC-wise, his code was faster to compiler, and his code worked. His code was flexible, because it wasn't overengineered. That was a valuable lesson for me in software design.
I think Enums are useful when values are known & a few in number.
Also, Enums are named constants in a way.
Enums don't have any other state stored with it (except the value of it).
Polymorphism would be helpful when you have different states of the conditions (and conditions require more state than relying in a single variable).
class Sunday extends DayOfWeek {}
class Monday extends DayOfWeek {}
class Tuesday extends DayOfWeek {}
class Wednesday extends DayOfWeek {}
class Thursday extends DayOfWeek {}
class Friday extends DayOfWeek {}
class Saturday extends DayOfWeek {}
Enums are fine.
It's not that enums are evil, it's switch statements. There's a long discussion of this in the C++ FAQ Book, but the gist is this: except for limited areas --- for example the interpretation of data coming in from a register on a device --- a big switch comb suggests that you're using data to distinguish among subtypes. In place of that, you ought to just use subtypes, gaining the compiler's help in keeping it correct, and also meaning the compiler will automatically add new cases when you (inevitably) change the set of cases.