Carlos Arboleda
https://carlosarboleda.co
GDG Cali
GDG Cali
Carlos Fernando Arboleda Garcés
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
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
GDG Cali
El "patrón" Singleton
STUPID
Imagen de Refactoring Guru
GDG Cali
Alto acoplamiento
UserController
StatusController
Cohesión
Acoplamiento
NotificationsController
Problemas
STUPID
GDG Cali
Código no testeable
STUPID
UserController
StatusController
StatusRepository
BD
NotificationController
fun inactiveUser(userId: Int)
GDG Cali
Código no testeable
Problemas
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.
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
GDG Cali
Duplicidad de código
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")
}
Principios SOLID
GDG Cali
Principios SOLID
GDG Cali
Clean Code Architecture
GDG Cali
SRP - Principio de responsabilidad única
SOLID
Detectar violaciones del SRP
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
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
Detectar violaciones del OCP
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
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
Detectar violaciones del ISP
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
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