views:

78

answers:

6

This is intended to be more of a discussion than an outright question, hence I've made it community wiki. When do you choose to break a method into smaller methods.

Say I have a method that has one purpose, i.e. to load a configuration file

public function configure($file){
    $info = path_info(file);
    $ext = $info['extension'];

    switch($ext){
    case 'ini':
    //code to parse ini files
    break;

    case 'xml':
    //code to parse xml files
    break;

    case 'php':
    //code to parse php files
    break;
}

}

personally, I'd break out to internal methods depending on the outcome of the switch - but say I have a more sequential method, a series of steps - i.e.

public function doSequence(){
    $stepOne = //some database access to retrieve values
    $stepTwo = //the result of some interpretation of values
    $stepThree = //a fresh database interaction based on the interpreted values
    $stepFour = //prep values for return
    return $values
}

So, what is your guiding principle for refactoring methods into the appropriate level of granularity?

A: 

You do not need to split a method, unless you want to reuse part of its code.

So, if you want to parse an ini-File in some other place, but don't need the whole configure function, then you break down the method.

In all other cases it doesn't make sense and should be left as is.

nikic
I strongly disagree with this answer. You can split up a method to make the code more readable for instance.
willcodejavaforfood
I do agree partially with him, as re-usability along with understandability should be there. Re-usability is the one aspect but function should be created with the intend to make code returning some functionality eg function should be created if my 20 line of code is doing parsing.
Shantanu Gupta
@willcodejavaforfood: I think code does not get way more readable if you split it down in functions. A simple example: In the last few days I often browsed through Zend code. And what is the greatest problem there? You can't find the definition for functions and macros, without some hours of effort! Now, in this case using functions and macros is correct, because the same code is reused and reused again; but why split code down if you don't need to? To irritate people who can't find the definition afterwards? No, thanks.
nikic
+1  A: 

You refactor a method when you cannot in good conscience say that you immediately understand completely what it does. Usually, that means it cannot have more lines than you can see at one glance. Calling several subroutines that do complicated things is okay even if you don't know exactly how they do it - after all, that is what abstraction is all about. Good descriptive subroutine names are very helpful here

Kilian Foth
+2  A: 

The basic situations when we refactor are:

  1. Reuse: A method is broken down to methods m1, m2, m3, m4. Out of these m2 and m4 are reused.

  2. Maintainability: The code in the method has grown up and will be hard to maintain.

  3. Logical grouping: A method performs a number of tasks and they are logically separable. Refactor the method to have as many logical groups as the tasks represent.

This list can get longer.

Kangkan
I would definitely add Testability to this list, although you cold argue that is covered under Reuse. But it's easier to test smaller units of functionality. What's easier to test gets tested. Well, OK: what's harder to test, more often doesn't.
Carl Manaster
I do completely agree to that. Testability is one of the most critical angle of looking at the code. Thanks.
Kangkan
+2  A: 

As a rule of thumb: if you describe your method in plain english and the description contains an AND, then it's a good candidate for breaking the method down into smaller chunks. Basically, a method should be understandable in less than 30s. If not, refactor.

In your example above, the code to parse the config files should not be inside this method or even in the same class. An Ini File Parser has one responsibility: parsing ini files. A YAML file parser parses YAML files and so on. Make the actual parsers into separate classes. Give them an interface. Then aggregate the parser into the Config class.

Gordon
A: 

Personally I refactor a method to make it as atomic as possible.

function loadFileAndSave()
{
   this.openFile("file name");
   this.removeLineBreaks();
   this.saveToDisk();
}

When coding this way you could almost get away with not doing low level documentation because the code reads well itself.

Am
A: 

This question is a lot more complex then it may seem and involves looking at the entire design of your software, rather than just your examples. There are good books on software design, and they cannot be summarized in a paragraph.

For example, it might not be a good idea to do the database accesses in the same object that is part of the model. Database access may be seen as more of a technical service, unrelated to the logic of your program, hence may need a separate class (and this aids reusability). But it all depends.

BobTurbo