Friday, December 01, 2006

Repetition

"This is the three Rs:
Repetition Repetition Repetition"

Repetition - The Fall

Bob Baillie's most recent blog entry reminds us of the Pragmatic Programmers' DRY principle: Don't Repeat Yourself. In other words, don't have duplicated code. Repetitious code is harder to maintain. It can also be harder to understand, because it is more verbose. Code shorn of duplication tends to be more modular and so lends itself to reuse.

These are all noble aims. The trick is to identify the duplicated code.

If you're using cut'n'paste you're probably making a design error


It is relatively easy to spot the duplication in a chunk of code like this:

...
x := get_some_value(4);
y := my_array.next();
call_some_routine(x,y,z);
if z = 0 then
x := get_some_value(5);
y := my_array.next();
call_some_routine(x,y,z);
if z = 0 then
x := get_some_value(6);
y := my_array.next();
call_some_routine(x,y,z);
...
end if;
end if;

There has to be a better way to manipulate that array. And those nested IF statements are the Suck! We can extract some of the duplicated code into a common function and call it from inside a loop

function dry (p1 in number, p2 in number) return number
is
n number;
return_value number;
begin
n := get_some_value(p1);
call_some_routine(n,p2,return_value);
return return_value;
end;
...
for i in my_array.first()..my_array.last()
loop
dry(i,my_array(i), z);
if z != 0 then
exit;
end if;
end loop;

Spotting repetitious code like that is fairly easy because the duplication is co-located and the same shape. Repetition can be harder to spot when the problem is code in different PL/SQL packages. Or when the duplication occurs in different shapes. For instance we might write an insert statement which assigns a sequence.nextval to the primary key column when the table owner has already created a before insert trigger to do precisely the same thing.

Digging out repetition


Repetition can arise in two ways: we are writing a new piece of functionality and we find ourselves doing cut'n'paste a lot. Or when related pieces of functionality get written by different people or at different times.

When we find repeating code in existing systems we can decide to leave it be or to remove it. Normally this happens because we are working on the program, during a bug fix or enhancement. So it makes sense to tackle it now by (dread word) refactoring the code. There are a number of different approaches to refactor the code, depending upon the precise form of the repetition. The important thing is to ensure that deduplicated code works exactly the same as the duplicated code. This means we need to have tests - preferably automated ones - which we can run before and after we change the code.

It is preferable to avoid repetitious code in the first case. Easier said than done. It can be hard to write shared code and get it right first time. Carl Manaster has written a very interesting article on a technique he calls Triangulation. The basic principle is:
  1. Write the code for the first case in full, to get the solution right.
  2. Write the code for the second case in full, to identify the common features.
  3. Extract the duplication into a common function and refactor the two cases to use it.

In other words, to avoid repetition we must first introduce it. Otherwise we cannot be sure that our common function correctly and completely captures the duplicated functionality. Carl's article again emphasises the importance of having automated unit tests to demonstrate that removing the repetition has not broken our code. There are just some things we can't repeat often enough.

6 Comments:

Blogger Niall said...

repetition, or even more insidiously *almost* repetition where two functions start off the same but diverge, will also happen as you add developers to a project.

1 December 2006 at 01:54:00 GMT-8  
Blogger gandolf989 said...

No offense but isn't this better than creating yet another function???
And as contrived code it should work the same. ; )

...
if z = 0
then
x := get_some_value(6);
else
x := get_some_value(4);
end if;
y := my_array.next();
call_some_routine(x,y,z);

1 December 2006 at 05:58:00 GMT-8  
Anonymous Andy C said...

No offence but where is the definitive listing of each different Fall lineup and memories of your 10 best concerts.

:-)

1 December 2006 at 07:13:00 GMT-8  
Blogger APC said...

Gandolf989

No offence taken. I probably should have invested more time in contriving the code :) As it is you'll just have to take my word for it...

Cheers, APC

1 December 2006 at 08:13:00 GMT-8  
Blogger APC said...

>> memories of your 10 best concerts.

Ah, well, I've only seen the Fall in concert five times so that will be a bit hard.

Cheers, APC

1 December 2006 at 08:15:00 GMT-8  
Blogger Sitora Nekto said...

One of the most useful stuffs!

4 April 2007 at 09:34:00 GMT-7  

Post a Comment

<< Home