3

The golden rule of code structuring is always said as splitting into many sub functions is a good thing. Though I noticed it becomes a problem in complex applications when a class of e.g. 10 bigger functions is split into a class with 50 functions. When development went so far, it becomes quite hard to understand the concept of the class, I mean you lose survey. This is often the case when you look into code of others or in your own code after some months past.

I've heard a function should not be bigger than what fits on the screen. And then you start to split into sub functions, and the following effect is a class of many functions where you lose survey.

One example: The Qt library internally uses private classes, which means every class has a d pointer to a private object of its own private class containing all those helper functions moved out to it.

Sometimes I also wished I could colour the background of the important code.

So what's your approach of keeping complex code structured but still understandable?

10
  • How about splitting up your class into 5 classes with 10 small functions each? Jun 11, 2013 at 8:56
  • @Florian Margaine: Good tip. And if you have too many classes in one module, split the module into several modules with at most 10 classes each. And if you have too many modules split your system into subsystems with at most 10 modules each. With subsystems, modules, classes, functions (i.e. 4 layers of abstraction) where each layer can contain at most 10 elements of the lower layer, you can have a system with 10^4 = 10000 functions. And, of course, each function should not have more than 10 lines (according to uncle Bob, no more than 4, 6 lines, but let's be generous). ;-)
    – Giorgio
    Jun 11, 2013 at 9:24
  • I have a controller class of 3500 code lines written in C, and about 70 functions. The whole thing uses a struct containing the data it works on. How do you handle that shared data after the split?
    – user93623
    Jun 11, 2013 at 9:31
  • @falkb: can't you pass the shared struct around using a pointer argument in the functions?
    – Giorgio
    Jun 11, 2013 at 9:33
  • What if the shared struct was private in the original class, and now it must be spread around the new set of classes; how can you keep it still private from the rest of the system?
    – user93623
    Jun 11, 2013 at 10:30

3 Answers 3

6

The canonical answer is to apply the Single Responsibility principle:

the single responsibility principle states that every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility.

Also, classes must have Cohesion

cohesion refers to the degree to which the elements of a module belong together. Thus, it is a measure of how strongly-related each piece of functionality expressed by the source code of a software module is

On the other hands, symptoms of low cohesion are exactly what you describe:

Disadvantages of low cohesion (or “weak cohesion”) are:

  • Increased difficulty in understanding modules.
  • Increased difficulty in maintaining a system, because logical changes in the domain affect multiple modules, and because changes in one module require changes in related modules.
  • Increased difficulty in reusing a module because most applications won’t need the random set of operations provided by a module.

As such, my advice is to review your class structure and refactor appropriately. You don't need to have very large classes, but certainly you want classes that do one clear thing right.

1

If you Feel Like it Could Be Splitted, Split It!

I don't really think of the balance as you seem to be considering it now: if it's bigger than 25 or 30 lines, and if I cannot get it almost instantly, then it can be refactored into smaller functions. Especially for big "torrent" functions that tend to be very imperatively written.

Use Descriptive Coding Conventions

Yes, the reader will need to jump through more hoops to understand the code, but if the functions are aptly named, it's an help. It's also an help if you get a stacktrace, as it gives you a semantic hint on what was happening.

Visual Aids

Sometimes I also wished I could colour the background of the important code.

Quite often, it's likely the "unimportant" code that's mostly internal and that you can separate into smaller functions can be put into private methods or extracted to utility classes.

While it has some disadvantages too (testing, coupling, etc...), it can help to quickly visually separate them from the rest, especially if you can change your code highlighting rules based on the access modifiers.

3
  • Do you know if and where I can set code highlighting rules (based on the access modifiers) in MSVC? I haven't found such settings.
    – user93623
    Jun 12, 2013 at 8:43
  • @falkb: no idea, haven't used the thing in over 2 years, and I wouldn't have touched these settings. You definitely can in Eclipse for Java development (and I think for C++ development as well), but I never really tweaked my Visual Studio: preferred to keep it in a state where it wouldn't freak out others.
    – haylem
    Jun 12, 2013 at 10:29
  • @falkb: see this answer, maybe, for some more help. However I had a quick look and I don't think Visual Studio allows for this kind of customization without additional plugins.
    – haylem
    Jun 12, 2013 at 11:04
0

So what's your approach of keeping complex code structured but still understandable?

My approach is to build several abstraction layers and have each component of each layer contain at most n elements from the lower layer, where n is a relatively small number (between 5 and 20) beyond which I lose the overview of the structure of a component. I cannot name an exact number but I would say 20 is already in the "too large" area for me. If you can keep more stuff in your mind, your number may be higher.

As an example, consider the 6 layers: system, subsystem, module, class, method, line of code. The names are chosen a bit arbitrarily, but it should give the idea. Also, let n = 10.

Take a module: it can have at most 10 classes (to be more flexible, let's say it can also have 12 or 15 classes, but definitely not 50 classes!). Apply the same principle to all layers: if a method has more than 10 lines it is doing too much, try to split it. If a class has more than 10 methods: split it. Etc.

With this scheme, you can organize roughly 10^5 = 100000 lines of code. Still, if you look down from a higher layer to a lower one, you still see some component that has at most 10 sub-items. If your project is larger, you can add extra abstraction layers.

The main idea is to split items that are too large to be understood and to push the complexity to the upper abstraction layers until you have enough abstraction layers to manage the complexity of the whole project.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.