I have come to believe something about refactoring that I haven't seen mentioned here, I know there are a lot of answers here already, but I think this is new.
I've been a ruthless refactorer and a strong believer in DRY since before the terms arose. Mostly it's because I have trouble keeping a large codebase in my head and partly because I enjoy DRY coding and I do not enjoy anything about C&P coding, in fact it's painful and terribly slow for me.
The thing is, insisting on DRY has given me a lot of practice in some techniques that I rarely see others use. A lot of people insist that Java is difficult or impossible to make DRY, but really they just don't try.
One example from a long time ago that's somewhat similar to your example. People tend to think java GUI creation is hard. Of course it is if you code like this:
Menu m=new Menu("File");
MenuItem save=new MenuItem("Save")
save.addAction(saveAction); // I forget the syntax, but you get the idea
m.add(save);
MenuItem load=new MenuItem("Load")
load.addAction(loadAction)
Anyone who thinks this is just crazy is absolutely right, but it's not Java's fault--code should never be written this way. These method calls are functions intended to be wrapped in other systems. If you can't find such a system, build it!
You obviously can't code like that so you need to step back and look at the problem, what is that repeated code actually doing? It's specifying some strings and their relationship(a tree) and joining the leaves of that tree to actions.
So what you really want is to say:
class Menu {
@MenuItem("File|Load")
public void fileLoad(){...}
@MenuItem("File|Save")
public void fileSave(){...}
@MenuItem("Edit|Copy")
public void editCopy(){...}...
Once you've defined your relationship in some way that is succinct and descriptive then you write a method to deal with it--in this case you iterate over the methods of the class passed in and build a tree then use that to build your Menus and Actions as well as (obviously) display a menu. You'll have no duplication, and your method is reusable... and probably easier to write than a large number of menus would have been, and if you actually enjoy programming, you had a LOT more fun. This isn't hard--the method you need to write is probably less lines than creating your menu by hand would be!
Thing is, in order to do this well you need to practice, a lot. You need to get good at analyzing exactly what unique information is in the repeated parts, extract that information and figure out how to express it well. Learning to use tools like string parsing and annotations helps a lot. Learning to be really clear about error reporting and documentation is very important as well.
You get "Free" practice simply by coding well--heck chances are that once you get good at it you'll find that coding something DRY (including writing a reusable tool) is faster than copying and pasting and all the errors, duplicated errors and difficult changes that type of coding causes.
I don't think I'd be able to enjoy my job if I didn't practice DRY techniques and building tools as much as I could. If I had to take a cut in pay not to have to do copy and paste programming, I'd take it.
So my points are:
- Copy & Paste costs more time unless you don't know how to refactor well.
- You learn to refactor well by doing it--by insisting on DRY even in the most difficult and trivial of cases.
- You are a programmer, if you need a small tool to make your code DRY, build it.
always
andnever
as red flags. There's a thing called "context", where thealways
andnever
rules, even if good in general, may not be that appropriate. Beware of software developers who deal in absolutes. ;)from itertools import chain; for name in chain(vip_list, guest_list): print(name)
.Special cases aren't special enough to break the rules. Although practicality beats purity.