From STUPI 

to SO   ID code

Carlos Arboleda

https://carlosarboleda.co

GDG Cali

🤪

💪

  • Co-Fundador & CTO LaManicurista
  • Co-Fundador & CTO MobileLab
  • Co-Organizador del GDG Cali
  • Android Developer
  • Backend Developer (Java/Node Js)

GDG Cali

Carlos Fernando Arboleda Garcés

  • Interés profesional.
  • Hay poca gente hablando de calidad de código.

 

 

Antes de aprender a escribir buen código debemos conocer cómo se ve el mal código.

GDG Cali

Motivación

Costo total

Desarrollo inicial

Mantenimiento

Entenderlo

Cambiarlo

Probarlo

Liberarlo

Esquema de costos de Kent Beck

El costo total del software

GDG Cali

Costo total

Desarrollo

Probarlo

Liberarlo

Esquema de costos de Kent Beck

Mantenimiento

Entenderlo

Cambiarlo

El costo total del software

GDG Cali

Cómo se reducen los costos?

GDG Cali

  • La ley de Pareto cuando se modifica código
    • 80% del tiempo entendiendo el código
    • 20% del tiempo escribiendo la solución
  • Singlet​on: Patrón singleton
  • Tight Coupling: Alto acoplamiento
  • Untestability: Código no testeable
  • Premature optimization: Optimizaciones prematuras
  • Indescriptive Naming: Nombres poco descriptivos
  • Duplication: Duplicidad de código

Code smell - Algo huele mal!

GDG Cali

class Singleton {
  public var title: String;
  
  private constructor() {
    this.title = "my singleton";
    Singleton.instance = this;
  }
  
  companion object {
    private var instance: Singleton? = null;
    
    fun getInstance(): Singleton {
      if (Singleton.instance == null) {
        Singleton.instance = Singleton();
      }
      
      return Singleton.instance!!;
    }
  }
}

val mySingleton1 = Singleton(); // Error
val mySingleton1 = Singleton.getInstance();
val mySingleton2 = Singleton.getInstance();

println("Singleton 1: " + mySingleton1.title);
mySingleton1.title = "modified in instance 1"
println("Singleton 2: " + mySingleton2.title);

Problemas

  1. Perdida de control
  2. Difícil de probar
  3. Múltiple responsabilidad

GDG Cali

El "patrón" Singleton

STUPID

Imagen de Refactoring Guru

GDG Cali

Alto acoplamiento

UserController

StatusController

Cohesión

Acoplamiento

NotificationsController

Problemas

  1. Reducción de portabilidad
  2. Difícil de leer
  3. Baja cohesión

STUPID

GDG Cali

Código no testeable

STUPID

UserController

StatusController

StatusRepository

BD

NotificationController

fun inactiveUser(userId: Int)

GDG Cali

Código no testeable

Problemas

  1. Estado dependiente de factores externos
  2. Alto acoplamiento
  3. No se inyectan las dependencias
class UserController {
  private notificationController: NotificationController;
  private statusController: StatusController;
  
  constructor() {
    this.notificationController = NotificationController();
    this.statusController = StatusController();
  }
  
  fun inactiveUser(userId: Int) {
    val user: User = this.getUserById(userId);
    if (user.status != StatusController.INACTIVE) {
      this.statusController.inactive(userId);
      this.notificationController.sendInactiveNotification();
    }
  }
}

STUPID

class UserController {
  private notificationController: NotificationController;
  private statusController: StatusController;
  
  constructor(
    notificationController: NotificationController,
    statusController: StatusController
  ) {
    this.notificationController = notificationController;
    this.statusController = statusController;
  }
  
  fun inactiveUser(userId: Int) {
    val user: User = this.getUserById(userId);
    if (user.status != StatusController.INACTIVE) {
      this.statusController.inactive(userId);
      this.notificationController.sendInactiveNotification();
    }
  }
}

GDG Cali

Optimización prematura

Donald Knuth decía que la optimización prematura es la raíz de todos los males.

  • No anticiparse a los requisitos
    • Complejidad esencial
    • Complejidad accidental
  • No hay una bala de plata

STUPID

GDG Cali

Nombres poco descriptivos

STUPID

Nombre pronunciables
// Mal
val yyyymmdstr: String = sdf.format(new Date(), "YYYY/MM/DD");
// Mejor
val currentDate: String = sdf.format(new Date(), "YYYY/MM/DD");


Lexico coherente
// Mal
getUserInfo();
getClientData();
getCustomerRecord();
// Mejor
getUser();


Nombre para arrays
// Mal
val fruit = listOf("manzana", "platano", "fresa");
val fruitList = listOf("manzana", "platano", "fresa");
val fruits = listOf("manzana", "platano", "fresa");
// Bien
val fruitNames = listOf("manzana", "platano", "fresa");

GDG Cali

Nombres poco descriptivos

STUPID

Nombres para booleanos
// Mal
val read: Boolean = true;
val write: Boolean = true;
val fruit: Boolean = true;
// Bien
val isRead: Boolean = true;
val canWrite: Boolean = true;
val hasFruit: Boolean = true;


Nombres para números
// Mal
val fruits: Int = 3;
// Bien
val maxFruits: Int = 5;
val minFruits: Int = 1;
val totalFruits: Int = 3

Problemas

  1. Dificultad al leer el código
  2. Dificultad al comunicarse con el equipo
  3. Necesidad de adicionar comentarios

GDG Cali

Duplicidad de código

  • No repetir
    • Duplicidad real
    • Duplicidad accidental

STUPID

DRY - Don't repeat yourselft

fun showReport(reportData: Data) {
  val reportFormatted = """
      Name: ${reportData.name}
      Created at: ${reportData.createdAt}
      Purchases: ${reportData.purchases}
      Conversion Rate: ${reportData.conversionRate}%
      """
  println("Showing report $reportFormatted")
}

fun saveReport(reportData: Data) {
  val reportFormatted = """
      Name: ${reportData.name}
      Created at: ${reportData.createdAt}
      Purchases: ${reportData.purchases}
      Conversion Rate: ${reportData.conversionRate}%
      """
  println("Saving report... $reportFormatted")
}
  • Single Responsability: Responsabilidad única
  • Open/Close: Abierto/cerrado
  • Lskov Sustitution: Sustitición de Liskov
  • Interface Segration: Segregación de interfaces
  • Dependency Inversion: Inversión de dependencias

Principios SOLID

GDG Cali

Principios SOLID

GDG Cali

Clean Code Architecture

GDG Cali

SRP - Principio de responsabilidad única

SOLID

Detectar violaciones del SRP

  • Nombre demasiado genérico (God Object)
  • Los cambios suelen afectar a esta clase.

  • La clase involucra múltiples capas de la arquitectura.

  • Número alto de imports.

  • Cantidad elevada de métodos públicos.

  • Excesivo número de líneas de código.

Nunca debería haber más de un motivo por el cual cambiar una clase o un módulo. – Robert C. Martin

  • Una clase o módulo solo debe tener un motivo para cambiar
  • Única responsabilidad  Un solo método.

GDG Cali

OCP - Principio abierto/cerrado

Todas las entidades software deberían estar abiertas a extensión, pero cerradas a modificación. - Bertrand Meyer

SOLID

  • Abierto para extender la funcionalidad creando nuevas clases
  • Cerrado para modificación de la clase actual

Detectar violaciones del OCP

  • Si violamos el principio SRP
  • Los cambios suelen afectar a esta clase.
  • La clase involucra múltiples capas de la arquitectura.

GDG Cali

OCP - Principio abierto/cerrado

SOLID

class SMSSender {
  sendMessage(
    msg: String, providerName: String
  ) {
    swhitch(providerName) {
      case Provders.INFOBIP:
        sendMessageInfobip(msg);
      break;
      case Provders.HABLAME:
        sendMessageHablame(msg);
      break;
    }
  }
  
  fun sendMessageInfobip(msg: String) {
    // ...
  }
  
  fun sendMessageHablame(msg: String) {
    // ...
  }
}
class SMSSender {
  // ...
  fun sendMessage(
    msg: String, providerName: String
  ) {
    IProvider provider = providerFactory
    	.get(providerName);
    provider.sendMessage(msg);
  }
}

interface IProvider {
  fun sendMessage(msg: String);
}

class InfobipProvider : IProvider {
  fun sendMessage(msg: String) {
    // ...
  }
}

class HablameProvider : IProvider {
  fun sendMessage(msg: String) {
    // ...
  }
}

Mal

Bien

GDG Cali

LSP - Principio de sustitución de Liskov

Las funciones que utilicen punteros o referencias a clases base deben ser capaces de usar objetos de clases derivadas sin saberlo. - Robert C. Martin

  • Respetar el comportamiento de las clases padres en las clases hijas

SOLID

Detectar violaciones del LSP

  • Si cambiamos el comportamiento de un método heredado de la clase padre.
  • Si existe métodos sobre escritos y sin implementación.
  • Si los métodos sobre escritos lanzan excepciones no definidas en la clase padre

GDG Cali

LSP - Principio de sustitución de Liskov

SOLID

GDG Cali

LSP - Principio de sustitución de Liskov

SOLID

GDG Cali

ISP - Principio de segregación de interfaces

SOLID

Los clientes no deberían estar obligados a depender de interfaces que no utilicen. – Robert C. Martin

  • No se debe depender de lo que no se necesita

Detectar violaciones del ISP

  • Si violamos los principios SRP y LSP.
  • Si hay métodos sin implementación en las clases clientes

GDG Cali

ISP - Principio de segregación de interfaces

SOLID

interface ButtonListener {
  fun onClick(button: Button);
  fun onLongClick(button: Button);
}

class Screen1 : ButtonListener {
  fun onClick(button: Button) {
    // ...
  }
  
  fun onLongClick(button: Button) {
    throw new Exception("Unsupported operation");
  }
}

class Screen2 : ButtonListener {
  fun onClick(button: Button) {
    throw new Exception("Unsupported operation");
  }
  
  fun onLongClick(button: Button) {
    // ...
  }
}
interface ButtonClickListener {
  fun onClick(button: Button);
}

interface ButtonLongClickListener {
  fun onLongClick(button: Button);
}

class Screen1 : ButtonClickListener {
  fun onClick(button: Button) {
     // ...
  }
}

class Screen2 : ButtonLongClickListener {
  fun  onLongClick(button: Button) {
     // ...
  }
}

Mal

Bien

GDG Cali

DIP - Principio de inversión de dependencias

SOLID

Los módulos de alto nivel no deben depender de módulos de bajo nivel. Ambos deben depender de abstracciones. – Robert C. Martin

Detectar violaciones del ISP

  • Si dentro de las clases de dominio se instancian otras clases
  • Si hay dependencias con librerías de terceros
  • Si hay dependencias directas 

GDG Cali

DIP - Principio de inversión de dependencias

SOLID

SMSSender

InfobipApi

- infobipApi

+ sendMessage()

- sendMessageInfobip()

+ sendMessage

Mal

SMSSender

InfobipApi

IProvider

+ sendMessage

+ sendMessage

+ sendMessage

Bien

🤓   Estudia sobre arquitectura y patrones
🐢   Empieza despacio pero hazlo real
😵   No intentes refactorizar todo de una vez
🔀   Escribe pruebas
🤔   Cuestiona todos tus hábitos
🏕️   Deja el parque más limpio de lo que lo encontraste
👴🏻   Frenar el entusiasmo (Principio KISS)
💸   No olvides a tus clientes
👨‍🎨   Conviertete en un artesano del software

GDG Cali

Algunas recomendaciones...

GDG Cali

Referencias

https://softwarecrafters.io

GDG Cali

Referencias

@cfarboleda

From STUPID to SOLID code

By Carlos Fernando Arboleda Garces

From STUPID to SOLID code

¡Calma! En esta charla no vas a recibir insultos. STUPID es solo un acrónimo para referirnos a 6 malas prácticas en las que se suele caer al escribir código. Vamos a aprender a reconocer el código STUPID, mejorarlo usando los principios SOLID y así comenzar a reducir la deuda técnica en nuestros proyectos. GDG Cali Day 2020

  • 669