Pergunta

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?

Foi útil?

Solução

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.

Outras dicas

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.

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.)

Licenciado em: CC-BY-SA com atribuição
scroll top