It's certainly not evil - though it does have drawbacks - it is one of those features of Magento that sets it apart and make it a flexible platform. However, like most things, too much of a good thing can quickly become very, very problematic. There are three ways of overriding core functionality:
An observer is always preferable. Observers don't have this limit of one-per-class. Observers can be slotted before or after desired controller actions. You can also tap into model load. They're pretty powerful.
However, they're not a one-size-fits-all solution and it's possible for you to not be able to pinpoint functionality.
Only one rewrite can exist at any time for a given class. Classes attempting to rewrite the same class over and over will have rewrite conflicts. Before you rewrite you want to determine:
- Is this a community module that will be used by n stores with n plugins installed?
- If so, are there common plugins which rewrite the same class you need to rewrite that perhaps you can code around?
- If not, are you sure that this is insular enough to not cause an issue?
If the above answers can be satisfied, use a rewrite.
Local Module Overrides
This is never preferable. This is stopgap only if you have a patch for a core issue which has not been issued by Magento yet.
A word on helpers
You are able to get some code reuse by creating a custom helper. I would avoid helpers that work across modules or in view files - if the helper doesn't exist and your view depends on it then it will throw an exception. I would prefer the rewrite first; however, if this is something that is reusable in potentially dozens of places you would then use a helper. A helper essentially used to keep your code DRY.
Which one you use is up to you. The order of precedence is as defined above in my opinion. Best of luck.