2 Cada cosita en su lugar
Edsger W. Dijkstra (1930-2002) es todo un personaje en el campo de las ciencias de la computación, no solo por la cantidad y calidad de sus aportaciones, sino también por su particular carácter y algunas frases lapidarias. También es responsable de introducir el principio de la separación de intereses, en su artículo de 1974 On the role of scientific thought.
Básicamente, el principio nos dice que los programas no deberían escribirse como una única pieza que resuelva el problema. En lugar de eso, debe organizarse en partes más pequeñas que se ocupan de tareas especializadas. O dicho de una forma más sencilla:
Diferentes partes del problema son tratadas por diferentes partes del programa.
Para ilustrarlo voy a usar un ejemplo exageradamente simplificado.
Consideremos este código en python:
1 #!/usr/bin/env python3
2
3 # Separation of concerns principle
4 # Different parts of the program address different concerns
5
6 import sys
7
8
9 if __name__ == '__main__':
10 print(sum(map(int, sys.argv[1:])))
Este programa simplemente suma los números que se le pasan como argumento:
1 ./main.py 20 30 40 # --> 90
No parece tener nada incorrecto, ¿verdad? De hecho, este tipo de one liners suele considerarse como especialmente inteligente. Sin embargo, para este artículo, este código pone de manifiesto un problema.
En primer lugar, cualquier programa básico tiene tres partes, y es la primera separación de intereses que vamos a considerar aquí:
- conseguir la información necesaria
- procesarla para obtener un resultado
- mostrar el resultado
En nuestro programa la única línea que tiene el programa se ocupa de los tres intereses. Esto quiere decir que si necesitamos modificar algo en relación con cualquiera de los tres intereses principales, tendremos que alterar todo el programa.
Da igual si se trata de mejorar algo en la presentación de resultados, obtener los números a sumar de otra fuente o utilizar un algoritmo diferente que haga el cálculo. Cambiar un aspecto del software implica hacer cambios que afectan a otros.
Podríamos simplemente separar cada uno de los intereses en una línea, algo así:
1 if __name__ == '__main__':
2 numbers_to_sum = map(int, sys.argv[1:])
3 sum_result = sum(numbers_to_sum)
4 print(sum_result)
Ahora hemos separado los tres intereses. Sin embargo, las líneas de código no representan bien las abstracciones que hemos definido. Para eso utilizamos unidades como las funciones:
1 #!/usr/bin/env python3
2
3 # Separation of concerns principle
4 # Different parts of the program address different concerns
5
6 import sys
7
8
9 def obtain_input_data():
10 return sys.argv[1:]
11
12
13 def sum_numbers(input):
14 return sum(map(int, input))
15
16
17 def show_result(result):
18 print(result)
19
20
21 if __name__ == '__main__':
22 show_result(
23 sum_numbers(
24 obtain_input_data()
25 )
26 )
Ahora tenemos módulos diferentes que se ocupan de cosas distintas. El programa principal simplemente las coordina. Podríamos cambiar cualquiera de ellas internamente sin afectar al resto del código.
Puedes decir que no hay mucha diferencia. Sin embargo, ahora cada interés está siendo atendido por un módulo diferente del programa. Partes diferentes del programa se ocupan de intereses diferentes, en un mismo nivel de abstracción del proceso completo.
El principio de separación de intereses está en la base de otros muchos principios de diseño, entre ellos el Single Responsibility Principle y Tell, don’t ask. De esa relación nos ocuparemos más adelante.
Pero si únicamente pudieses quedarte con una idea de este libro, quédate con este principio.