3

I designed a function which, afterwards, I noticed it is clearly doing 2 very different tasks in the same time.

So now I am thinking to split it into 2 different functions, each one of them doing only what it should do (SRP). Obviously, one of these functions will call to the services of the other one.

But I wonder if this is really a good decision because I tend to think this call increases tight coupling as per this (?)

So my question is: does SRP increase tight coupling?

7
  • 2
    No, it’s quite the opposite since your 2 functions can now be reused by other functions. Aug 25, 2018 at 6:24
  • 4
    @BillalBEGUERADJ: The highest form of coupling between pieces of code is to have them in the same function. That coupling is so high, the code might as well be considered to be one piece. Aug 25, 2018 at 6:52
  • 2
    By splitting the functions, at least one of those functions will have absolutely no dependency on the other. You could decouple them altogether by using higher-order functions, in which case neither function would depend upon the other. Aug 25, 2018 at 8:19
  • 1
    "a function is too dependent on the function it calls, is not this a tight coupling increase somehow?" First, compared to what? Second, the dependency between one function that calls another is on the signature, behavior as well as the name of the function. Of those three, the name dependency is the tightest, since it prevents substitution. This can be alleviated by injecting the function, e.g. supply function as parameter (i.e. higher order functions as per prior comment).
    – Erik Eidt
    Aug 25, 2018 at 15:36
  • 1
    @BillalBEGUERADJ No, you sitill have coupling but less than before. Aug 25, 2018 at 16:57

3 Answers 3

4

SRP applies to classes and modules:

There's a lot of confusion out there about SRP. Here an article of Uncle Bob, who "invented" SRP, that explains it much better than I could. In short:

  • SRP is not about what the class does (the responsibility of the class). So it doesn't force you to break down classes into tightly coupled twins just because they would perform several things.

  • SRP is about the reason to change (the responsibility for the class). It encourages encapsulation and facilitates the principle of least knowledge, which combined should lead to less coupling.

Should functions do one thing ?

Is his book Clean Code, Uncle Bob recommend that functions inside a class or a module "Do one thing". It may sound like SRP but is something very different. Together with "Keep it small", and "One level of abstraction per function", it promotes a proper functional decomposition into smaller parts that are easier to understand and maintain.

Of course, a function will depend on the sub-functions that were created to decompose it. Inside the same class, coupling is unavoidable. But this is not a problem as long as you don't expose these internal details to the outside world.

1
  • 1
    @BillalBEGUERADJ Exactly ! The "do one thing" (principle for functions) is often confused with SRP (principle for classes and modules), because "one" suggests "single". But these are two different principles with different purposes. So SRP does not increase coupling; on contrary. But the "do one thing" may create coupling (I prefer in this case to call it "dependencies", since it is unidirectional) inside a class or module, without increasing coupling between different classes
    – Christophe
    Aug 25, 2018 at 18:01
3

One of the key benefits of low coupling is that it reduces the number of things you need to consider simultaneously to understand a single component of a system -- allowing among other things for easier debugging, easier maintenance, and better extendability. A reasonable way to answer your question is to consider whether or not SRP would improve your code with respect to these metrics.

My claim is that SRP has the ability to reduce coupling, but that reduced coupling is not guaranteed. I'll illustrate this with two scenarios.

Scenario 1 (don't do this)

  • Start with one mega function foo().
  • Note that foo() violates SRP, so re-factor it into bar() and baz().
  • As your application grows, other parts of the code wind up depending on bar() and baz().
  • Customers and use cases change over time, so eventually foo() needs a modification. Most of the logic in foo() is implemented in bar() and baz(), so to change behavior in foo() we change the behavior in bar() and baz() instead.
  • Now everything breaks.

Even though SRP has the ability to reduce coupling (at least I'm claiming it does), in this example the programmer is treating foo(), bar(), and baz() as a single unit AND they have the additional complexity of that unit being split between multiple function definitions and maybe even multiple files. The SRP was in name only and had no real benefits because of how the functions were being used.

Scenario 2 (reduced coupling with SRP)

  • Start with one mega function foo().
  • Note that foo() violates SRP, so re-factor into bar() and baz().
  • As your application grows, other parts of the code wind up depending on bar() and baz().
  • Eventually foo() needs a modification, but we don't have an appropriate low-level component lying around with the desired functionality, so we make a new function bizz().
  • Now foo() consists of bar() and bizz(), and everything else still works fine.

In this example, it is easy to reason about the behavior of foo() because we're treating its two constituent parts as atomic. They have a well-defined behavior, and combined they make up foo(). When those behaviors are insufficient, we make a new atomic component with the desired functionality. This approach easily leads to code re-use and prevents API-breaking changes. Moreover, all the components are small enough and can be easily reasoned about and proven to be correct.

Extras: Even though SRP can decrease coupling in a sense, if functions are not grouped appropriately it has the potential to have every module in an application depending in some way or another on every other module. Designing the entire system to be loosely coupled is every bit as important as designing functions to be loosely coupled, and for roughly the same reasons.

3

From any reasonable point of view, the action of extracting of part of function into another function does not cause any harm which can attributed to a coupling. What may be bad is that later somebody reuses the extracted functions without careful consideration is it really the same functionality (as in scenario described in another answer). So you should consider it carefully, and limit usage of the sub-functions, by using the measures provided by your language (private methods etc.)

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.

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