1

I was assigned a refactoring.

We have several modules having similar functions with some differences. My task is to extract common portions of code for DRY principle.

I am a little lost how to do it.

I could just cut&paste similar fragments of code, extracting them into separate functions, but if I do it this way mechanically, then the declarations and usage of local variables of the fragmented functions may happen to fall into different functions, what could lead to invalid code.

Could you do some practical advice how to handle this issue. I'd wish a step-by-step guide on refactoring such code with local variables.

And also: In some reason my boss wants the resulting code not to be object oriented. I think this can be solved by passing fragments of code as function pointers.

I can't show a code example for my question, because the real code is closed-source and I can't write a minimal example, because the entire issue is dealing with long functions.

One more note: We write in Perl.

  • Some idea: I may move the declarations of variables lower in the source more near to the usage, before splitting the functions into fragments. Is it a good idea? – porton Mar 5 '17 at 18:36
  • I started this work. It now looks like that a right strategy is first extract small fragments of code – porton Mar 6 '17 at 15:16
3

You should look for what they have in common, e.g. they all open an SQL connection, they all need to handle errors and close resources at the end, they all call some API in the middle, etc. Then focus on separating what is constant from what is variable.

There are a few patterns that help deal with this. Consider the

Strategy Pattern

Template Pattern

  • Egad! Please, no more template pattern! Don't forget decorator pattern and good old functional composition. – candied_orange Mar 5 '17 at 19:59
1

Find out what parts are common, and separate the uncommon parts into separate functions. Then combine them.

Suppose that you have two functions:

def foo_y(x):
   <fragment A dealing with x>
   <fragment X>

def foo_z(x):
   <fragment A dealing with x>
   <fragment Y>

Now it's easy to create functions fragment_a, fragment_x, fragment_y and combine them into foo_x and foo_y.

You may have a layered structure:

def foo_y(x):
   <fragment A dealing with x>
   b = <fragment X>
   <fragment B dealing with b>

def foo_z(x):
   <fragment A dealing with x>
   b = <fragment Y>
   <fragment B dealing with b>

Extract fragment_x and fragment_y s functions.

You can now write:

def parametric_foo(x, handler_of_b):
  <fragment A>
  b = handler_of_b(...)
  <fragment B>

Combining these two approaches should help you with the majority of the task.

  • Good, but you haven't answered my question: How to ensure that variable declarations will be extracted into the same function which uses them? I yet not quite understand how to do it in practice, because if I do fragments cut&paste it may split variable usages from its declaration. What is particular a better thing than cut&paste? – porton Mar 5 '17 at 21:07
  • 1
    @porton: In a compiled language, the compiler will tell you. In an interpreted language, your test suite will (likely) tell you. IDEs often allow to factor out a block of code as a function, adding proper parameters to pass the needed variables and eliminating now-disused variables. In any case, human oversight is desirable. If the resulting functions are still complicated, continue the process until the data flow is obvious. – 9000 Mar 5 '17 at 23:03
  • I don't ask how to check whether a given refactoring is correct, but how to make the refactored program text – porton Mar 6 '17 at 12:37
  • @porton: I'm afraid there's no universal tool. A language-specific IDE often helps. Otherwise, I use text search and my judgment when facing a task like this. It's like solving a puzzle (as much of programming is in general). – 9000 Mar 6 '17 at 13:03
  • I am not about a special tool, but how to do it with a regular text editor. What the programmer should do? (not what a tool should do) – porton Mar 6 '17 at 16:12
0

It can be done replacing all (or most of) local variables with a structure/record (hash in Perl) having these variables as members.

Then it is enough to pass to the extracted fragment function a pointer to the structure, without the need to pass (reference to) every local variable.

This strategy makes easy to extract fragments of code into separate functions.

This approach also resembles object oriented programming: the structure I tell about is like an object (whose purpose is to keep intermediate data for calculating the refactored function).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.