Slides de la charla del open space de wecode 2019.
Cuento qué reglas seguimos en @plasticscm para crear el código más simple posible. Cosas como que no usamos herencia, ni sobrecarga de métodos y que nuestro mantra es "pasa parámetros y todo irá bien"
5. Tenemos clientes de todo tipo
Enormes, medianos, pequeños
Financial Sector IT & Embedded SW Automobile & Logistics
Games & 3D DesignDefense & Gov.
Utilities & Logistics
Medical Devices
10. Pasar parámetros evita casi todos los males
• Adiós a los Singletons.
• Las dependencias y efectos laterales se hacen explícitos.
• Es más fácil de testear todo (casi funcional).
Volveremos a ello más adelante
11. Y nos hemos puesto unas reglas
• Jamás usamos herencia (no adoramos la OOP precisamente)
• Nunca usamos sobrecarga de métodos. Ni constructores.
• Datos y comportamiento… bien separados! No más Revision.Save()
• Nada de “elses”.
• Muchas subclases e interfaces que pertenecen a clases.
• Nada de “doers, handlers”. Mejor “SaveRevision.Save()” que “RevisionSaver”.
Que me gustaría compartir y … discutir
12. ¿Cuál es el principal atributo que debe
tener el código? – un mantra
“Programs must be written for people to read, and only incidentally for
machines to execute.”
“The Structure and Interpretation
of Computer Programs”.
@psluaces
18. ¿Cómo guardas objetos?
• Datalayer.Save(Object o) parecía mala idea.
• Era mucho mejor Object.Save(), se guardaba solito.
• Y con miles de objetos? Pues nada, miles de Save()…
No íbamos a ser tan cutres como para hacer una función en la era de los ORM, verdad?
19. Por supuesto no funcionó
• Datalayer.Save(Object o)
• Que dentro tenía un SWITCH enorme para guardar cada objeto de una forma… y con el tiempo se
fueron optimizando y separando…
• Y era todo un desastre, porque querías buscar desde qué sitios se guardaban ramas y… no había
manera, era todo Datalayer.Save “genérico”.
Y pasamos a la segunda peor versión
20. Ahora lo hacemos tan explícito…
• Por supuesto no más herencia.
• Y los métodos son de este tipo:
• Datalayer.SaveRevision(Revision r)
• Datalayer.SaveBranch(Branch b)
• Etc
• Tan explícito y sencillo como podemos.
• Sí, son muchos métodos… y qué?
Que no hay lugar a dudas
22. La herencia es el root of all evil
• El 99% de las veces la herencia se usa de forma incorrecta.
• O bien se usa para “compartir código común” – error, para eso está la composición (no cumple con
subtipado).
• O bien para “montar castillos terribles”, para “generalizar”, para “cuando cambie”.
Porque es dificilísimo usarlo bien
23. La herencia es un problema en sí mismo
• Muy pocos programadores desarrollan frameworks, si no aplicaciones, luego no deben
preocuparse de romper compatibilidad si no de hacer código legible y sencillo.
• No generalices hasta que tengas al menos 3 ejemplos: típica capa abstracta de acceso a base de
datos, para acceder a un único tipo de base de datos! :-S
A menos que hagas frameworks… pero casi nadie hace eso
24. Un ejemplo de herencia *real*
Para un triste progreso!!!
FiniteProgress
Progress
abstract
FastUpdateProgress
IFiniteProgress
FastUpdateApplierProgress
CLIFastUpdateUpdateApplierProgress
CLIFastUpdateProgress
25. Que se quedó así
Si es algo simple… el código debe ser simple
FiniteProgress
Progress
abstract
FastUpdateProgress
IFiniteProgress
FastUpdateApplierProgress
CLIFastUpdateUpdateApplierProgress
CLIFastUpdateProgress
IFastUpdateProgress
FastUpdateApplierProgress FastUpdateProgress
26. La herencia destruye neuronas
• Estas leyendo código y ves un “abstract” o un “protected” o un “virtual”…
• Y de inmediato piensas “cuidado”! Alguien está implementando/extendiendo/modificando ese
comportamiento.
• Cerebro en modo peligro.
• Y a veces es sólo “por si acaso”, para hacerlo extensible… pa na!
• Un “private” nunca te pone en ese modo, está clarísimo que nunca, nunca lo llamará nadie. Te da
confianza, es bueno, fácil, no gastas cerebro para nada.
Y quita confianza
32. No reutilices antes de tiempo
Es casi como salir de Matrix
• Quién usa EncryptPassword? Solo CredentialsDialog? Pues el método ahí tranquilo y privado.
• “Es que eso debería estar en un sitio común y”…
• No!
• Común cuando se use desde más de un sitio, mientras tanto, en un único sitio. Evita complicarte la
vida diseñando “para luego”.
34. Es más fácil hacerlo bien…
• La optimización prematura es un problema ampliamente aceptado.
• Pero se unen otros no tan comúnmente aceptados:
• La reutilización prematura – “para cuando cambie” – salimos tatuados con esto de la universidad. Escribe
código para “hoy” no vaya a ser que no llegues a mañana. (Torvalds dixit!)
• La generalización prematura – intentar hacer todo “genérico” en lugar de terriblemente simple.
Es MUCHO más fácil hacer cosas concretas, no genéricas, resolviendo el problema de HOY. Resuelve el
problema de hoy e igual estás en el negocio para resolver el de mañana. Resuelve el de mañana y no
llegarás.
Una vez que tienes claro que está bien
36. Y nos hemos puesto unas reglas
• Jamás usamos herencia (no adoramos la OOP precisamente)
• Nunca usamos sobrecarga de métodos. Ni constructores.
• Datos y comportamiento… bien separados! No más Revision.Save()
• Nada de “elses”.
• Muchas subclases e interfaces que pertenecen a clases.
• Nada de “doers, handlers”. Mejor “SaveRevision.Save()” que “RevisionSaver”.
Que me gustaría compartir y … discutir
37. Adiós overload
Que se pueda hacer no quiere decir que sea obligatorio
Ganas claridad de código.
No hay que explicar los constructores ni los métodos, el nombre te dice lo que hacen.
Además, muchos de los métodos sobrecargados no se usan o se pueden evitar pasando un parámetro
extra. Menos métodos => más simple.
Ser flexible NO es bueno => no hacemos frameworks.
38. Datos y métodos separados
• No es orientado a objetos.
• Pero una vez te liberas de Matrix, vives muy bien.
• Tienes una clase Config.Data y un método Save(Config.Data d).
• Fácil y sencillo.
• Y si la clase Config.Data no permite cambiar sus valores… mejor.
Usamos muchas clases solo de datos… y qué?
39. Pasa parámetros y no uses globales
No usar variables globales – como en la primera clase de programación
40. Pasa parámetros y no uses globales
No usar variables globales – como en la primera clase de programación
MAGIA!!!
41. Es que no son variables globales… son miembros!
• Es que los objetos tienen estado.
• Y así es más fácil porque “no pierdes tiempo” pasando parámetros.
• Y son intrínsecos al objeto.
• Y sí, no hace falta que se pasen.
• Y …
The OOP true religion
43. Lo siguiente es que todo son funciones
De esto
MergeCalculator calculator = new MergeCalculator(diffs, revs, blah);
MergeResult r = calculator.Calculate();
Pasas a esto:
MergeReult r = CalculateMerge.Calculate(diffs, revs, blah);
No hay estado, ni constructor… y se prueba facilísimo
Empiezas pasando parámetros y luego…
44. Hemos plagado nuestro código de métodos
estáticos
• Y todo es mucho más fácil.
• No necesitas mocks, ni inyectar dependencias para probar… solo pasar tristes y sencillos
parámetros.
• Mucho del estado incontrolable de hace años, son ahora sencillísimos parámetros.
• Muchas veces es solo cambiar la forma de pensar, y te das cuenta de la absurdez:
ClickCommand command = new ClickCommand(dialog, status, blahblah, blah);
Command.Execute();
Con lo fácil que es: ClickCommand.Execute(dialog, status, blahblah, blah); Y adiós!
Y clases que en realidad son funciones complicadas con varios pasos
45. Adiós a la complejidad gratuita
• Descubrimos que muchas de las partes complejas lo eran por deporte.
• Por aplicar patrones para tener comandos reusables… en lugar de llamar a una simple función y
ya.
• Muchas jerarquías sobraban.
• Todo podía ser más sencillo.
46. Un ejemplo en una GUI
Qué debe hacer el OnClick del botón OK?
Welcome to PlasticSCM ×
OK Cancel
Use SSL
Enter the name/IP and portof your server (e.g: myserver.mynetwork.net:8087)
localhost:8087
Configure your credentials
pablo *********** Check
Credentials checked OK
47. Un ejemplo en una GUI
Qué debe hacer el OnClick del botón OK?
Welcome to PlasticSCM ×
OK Cancel
Use SSL
Enter the name/IP and portof your server (e.g: myserver.mynetwork.net:8087)
localhost:8087
Configure your credentials
pablo *********** Check
Credentials checked OK
• Pues debería recibir un ICommand que ejecute una
acción.
• De ese modo haces que la GUI no dependa de algo
concreto y…
• Para qué???
• El click del botón llama a código totalmente sencillo y
se ve lo que hace, sin cuarenta indirecciones.
48. Una fácil, salir del if cuanto antes
Es un pecado en “programación estructurada”… pero… y qué?
49. Nombres de clases
• FileReader => ReadFile
• ProgressCalculator => CalculateProgress
Más fácil, más humano, más “hace algo sencillo” en lugar de es “un calculador de progreso”
Es casi una obsesión
50. Subclases
Esto va en contra de las normas de buenas prácticas en .NET :-O
Usamos mucho esto, en lugar de polucionar el mundo con clases “de primer nivel” que en realidad no
viven por sí solas… y hacemos lo mismo con las interfaces.
51. Interfaces definidas dentro de clases
Tú necesitas la interfaz, tú la defines. NO el que la implementa, el que la necesita para funcionar
52. Ejemplo de pasar parámetros
• Cómo accedemos al API en cada sitio? Un singleton? RestApi.Get().Blah() ???
• No, pasamos el REST API a cada clase.
• Puede parecer pesado => pero las dependencias se hacen clarísimas en lugar de ocultas.
gmaster – usa un REST API para comunicarse con gmaster.io
53. Visibilidad mínima
• Nada es “para si luego”.
• Se hace solo lo que uses *ahora*.
Si puede ser private, mejor que internal, mejor que public
54. Properties
• Regla: una property NUNCA realiza una operación.
• Ejemplo: Branch.Name simplemente devolverá un valor que ya tenga el objeto, jamás disparará
una llamada al servidor (esto nos ha pasado).
En C# las properties son algo que ha estado desde siempre
55. Ermitaños del código
• https://pragprog.com/book/kpiod/interface-oriented-design
• Chatty object oriented interfaces – un error que cometimos hace mucho tiempo (¡porque parecía
que era la forma de hacerlo bien!)
• En red: siempre “document oriented” -> minimiza roundtrips. No es “transparente” pero está bien
Prefiero un triste socket que Remoting, WCF, Protocol Buffers, DCOM, RMI, etc, etc
57. Bibliografía
• Clean Code – Robert C. Martin
• Implementation Patterns – Kent Beck
• Code Complete – Steve McConnell
58. Adoramos el código
• The Code is the Design – Jack W. Reeves -
http://www.developerdotstar.com/mag/articles/reeves_design_main.html
59. Conclusión
• Cuantas menos cosas complicadas, mejor.
• No pasa nada por no usar lo último del nuevo C#. Es duro quitarse el complejo.
Escribimos código que cualquiera pueda entender