Thursday, 26 June 2008

Copy and Paste is Not your friend!!

I heard that quote a few years back from a trainer on a .Net training course and the more time goes by, the truer it becomes. Almost all of my code errors are related to copying and pasting code and forgetting to modify it for the new location. It is not surprising then that there are techniques to avoid copying and pasting or "inheriting from the clipboard" as I've also heard it.
Firstly, if you are copying and pasting, you might well be hiding a required function. Even a simple repetition of 2 lines could be a function:
Rectangle rect = new Rectangle();
rect.Width = 100;

When copied several times (but almost invariably getting renamed in the process) is prone to bugs even though it is surely so simple it is idiot proof!? A "factory method" is simply a method that creates an object for you, hiding the detail, and then passes it back, e.g.:
Rectangle Create100WideRect()
{
Rectangle rect = new Rectangle();
rect.Width = 100;
return rect;
}

Note that although the function is 6 lines instead of the original 2, you only need to replace 3 calls to save room and more importantly there is no grey area, every single place that needs a 100 wide rectangle can call the factory method. Note that if you need to make slightly different instances then either call the factory method and modify the object afterwards or pass the difference into the function and let the function customise it for you. If the items you are creating exist in another library then try and avoid the need for a local variable by passing the create function directly to whatever needs the object like:
ProcessRectangle(Create100WideRect());

and you haven't tied the user to the creation of the object.
The second common reason for copy and paste is when you really need another class. If you are doing two thing similarly, maybe you need a base class doing all the same things and then two sub-classes that specialise the code. Again, doing this means that no two blocks of code are the same because the same bits are put into a base class in 1 place! Just because you think that the two blocks of similar code belong in two functions in the same class doesn't mean you can't create some helper classes purely to use for those two functions rather than:
if ( something ) Function1(); else Function2();

You could instead do:
CurrentHelper->DoFunction();

with no ifs, no buts and solid separation of functionality. You can pass in any variables these functions need to use from your class as constructor or function parameters.
The third common use for copy-and-paste like the second is when your single class actually needs to be abstracted into some sort of hierarchy. Suppose for instance you have a large switch statement with lots of similar code, this could be a place to use a pattern, perhaps "chain of responsibility" or "state pattern" where each case block becomes an object and the code that is the same for multiple blocks can be abstracted into base classes. Remember that two blocks of code is double the bug risk so keeping things in one place means there is only one place to fix it!
Post a Comment