0% encontró este documento útil (0 votos)
49 vistas11 páginas

Principios Solid

La deuda técnica se refiere al costo adicional causado por elegir soluciones rápidas en lugar de efectivas. Existen diferentes tipos de deuda técnica y es importante que los equipos entiendan cómo gestionarla de manera eficiente mediante la refactorización del código y pruebas automatizadas. El código limpio ("clean code") es fácil de leer y mantener, resolviendo problemas sin complejidad innecesaria.

Cargado por

alexis torres
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como ODT, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
49 vistas11 páginas

Principios Solid

La deuda técnica se refiere al costo adicional causado por elegir soluciones rápidas en lugar de efectivas. Existen diferentes tipos de deuda técnica y es importante que los equipos entiendan cómo gestionarla de manera eficiente mediante la refactorización del código y pruebas automatizadas. El código limpio ("clean code") es fácil de leer y mantener, resolviendo problemas sin complejidad innecesaria.

Cargado por

alexis torres
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como ODT, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 11

DEUDAS TÉCNICAS

La deuda técnica es el costo del trabajo adicional causado por la elección de la solución más
rápida en lugar de la más efectiva. Si bien, en ocasiones, la deuda técnica vale la pena, es
importante que tu equipo comprenda las ventajas y las desventajas de las decisiones apresuradas
y cómo gestionar el re trabajo de una manera eficiente.

Existen varios tipos de deudas técnicas

• imprudente
◦ cuando el desarrollador sabe que tiene el error pero no tiene tiempo de resolverlo y
solo copia ay pega.
• Inadvertida
◦ generada por desconocimiento de la deuda o falta de conocimientos sobre el tema
• prudente
◦ cuando sabemos que la deuda existe y no se esta trabajando sobre ello

¿Como pagar una deuda técnica?


La deuda técnica se para con refactorizacíon de código , es el proceso que tiene como objetivo
mejorar el código din alterar su comportamiento para que sea mas entendible y tolerante a
cambios.
Usualmente para que una refactorizacíon fuerte tenga el objetivo esperado, es imprescindible
contar con pruebas automáticas.
Con esto evitamos el famoso “si funciona no lo toques”.

Nota:
La mala calidad en el software siempre la acaba pagando o asumiendo alguien.
Ya sea el cliente, el proveedor con recurso o el propio desarrollador dedicando tiempo a re
factorizar o malgastando tiempo programado sobre un sistema frágil.

CLEAN CODE

Se considera que el código es limpio (en inglés, clean code) cuando es fácil de leer y entender. Si
resuelve los problemas sin agregar complejidad innecesaria, permitiendo que el mantenimiento o
las adaptaciones, por algún cambio de requerimiento, sean tareas más sencillas, entonces
estamos hablando de “clean code” Para crear código limpio hay que conocer y poner en práctica
un conjunto de principios o técnicas de desarrollo que nos ayudarán a evitar los code smells, es
decir, esos síntomas de un programa que te dan el indicio de que existe un problema más
profundo.

Algunas de las recomendaciones para clean code son

• Nombrar tus variables con un nombre que tenga significado y que sepamos para que es y
que valores contiene
• Nombrar las clases dependiendo que es lo que va ser y para que va ser usada

Pagina: 1
• Nombrar funciones con valores entendibles para que sepamos para que funciona y que es
lo que va hacer dicha función.

// Buenas practicas de Clean Code en funciones


function getBbooks() {
throw new Error('Function not implemented.');
}

function getBooksByUrl( url: string) {


throw new Error('Function not implemented.');
}

function getSquareAreaBySide( side: number ) {


throw new Error('Function not implemented.');
}

function printJob() {
throw new Error('Function not implemented.');
}

• parámetros y argumentos bien definidos, es recomendable no tener mas de tres


argumentos definidos por función, ¿Que podemos hacer si tenemos mas de tres
argumentos?
◦ Podemos realizar un objecto con los valores que va utilizar la función
// mala practica de CLEAN CODE
function sendEmail (toWhot:string,from:string,subject:string,body:string,apikey:string){
// .... funcion que va realizar el envio de email
}

// buena practica de CLEAN CODE


type SendEmailOptions = {
toWhot:string,
from:string,
subject:string,
body:string,
apikey:string
}
function sendEmail ({toWhot,from,subject,body,apikey}:SendEmailOptions){
// .... funcion que va realizar el envio de email
}

Recomendaciones para funciones


• Las funciones deben ser lo mas simple posible
• Las funciones deben tener tamaños reducidos
• Funciones de una solo linea sin causar complejidad
• Las funciones deben tener menos de 30 lineas de código
• Priorizar el uso de la condicional ternaria
// MALAS PRACTICAS EN CLEAN CODE NO Priorizar el uso de la condicional ternaria
const getPayAmount = ({isDead = false, isSeparated = true, isRetired = false}) => {
let result
if (isDead) {
result = 1500;
} else {
if (isSeparated) {
result = 2500;
} else {
if (isRetired) {
result = 3000;

Pagina: 2
} else {
result = 4000;
}
}
}

return result;
}

// BUENAS PTRACTICAS EN CLEAN CODE Priorizar el uso de la condicional ternaria


interface PayAmount {
isDead: boolean,
isSeparated: boolean,
isRetired: boolean
}

const getPayAmount2 = ({isDead, isRetired, isSeparated}: PayAmount): number => {


let result: number
isDead && (result = 1500);
isRetired && (result = 3000);
isSeparated && (result = 2500);
retur

// MALAS PRACTICAS EN CLEAN CODE FUNCIONES NO SIMPLIFICADAS


(() => {
function isRedFruit( fruit: string ): boolean {

if ( fruit === 'manzana' || fruit === 'cereza' || fruit === 'ciruela' ) {


return true;
} else {
return false;
}
}
function getFruitsByColor( color: string ): string[] {

if ( color === 'red' ) {


return ['manzana','fresa'];
} else if ( color === 'yellow') {
return ['piña','banana'];
} else if ( color === 'purple') {
return ['moras','uvas']
} else {
throw Error('the color must be: red, yellow, purple');
}
}
let isFirstStepWorking = true;
let isSecondStepWorking = true;
let isThirdStepWorking = true;
let isFourthStepWorking = true;

function workingSteps() {
if( isFirstStepWorking === true ) {
if( isSecondStepWorking === true ) {
if( isThirdStepWorking === true ) {
if( isFourthStepWorking === true ) {
return 'Working properly!';
}
else {
return 'Fourth step broken.';
}
}
else {
return 'Third step broken.';
}
}
else {
return 'Second step broken.';
}

Pagina: 3
}
else {
return 'First step broken.';
}
}

console.log({ isRedFruit: isRedFruit('cereza'), fruit: 'cereza' }); // true


console.log({ isRedFruit: isRedFruit('piña'), fruit: 'piña' }); // true

console.log({ redFruits: getFruitsByColor('red') }); // ['manzana', 'fresa']


console.log({ yellowFruits: getFruitsByColor('yellow') }); // ['piña', 'banana']
console.log({ purpleFruits: getFruitsByColor('purple') }); // ['moras', 'uvas']
// console.log({ pinkFruits: getFruitsByColor('pink') }); // Error: the color must be: red, yellow, purple

console.log({ workingSteps: workingSteps() }); // Cambiar los valores de la línea 31 y esperar los resultados

})();

// BUENAS PTRACTICAS EN CLEAN CODE FUNCIONES SIMPLIFICADAS


(() => {

function isRedFruit(fruit: string): boolean {


const fruitsName = ["manzana", "cereza", "ciruela"];
return fruitsName.includes(fruit.toLowerCase())
}

function getFruitsByColor(color: string): string[] {


const fruitsByColor = {
red: ['manzana', 'fresa'],
yellow: ['piña', 'banana'],
purple: ['moras', 'uvas']
}
if(!Object.keys(fruitsByColor).includes(color)){
throw Error('the color must be: red, yellow, purple');
}
return colors[color];
}

let isFirstStepWorking = true;


let isSecondStepWorking = true;
let isThirdStepWorking = true;
let isFourthStepWorking = true;

function workingSteps() {
if(!isFirstStepWorking) return 'First step broken.';
if(!isSecondStepWorking) return 'Second step broken.';
if(!isThirdStepWorking) return 'Third step broken.';
if(!isFourthStepWorking) return 'Fourth step broken.';
return 'Working properly!';
}

console.log({isRedFruit: isRedFruit('cereza'), fruit: 'cereza'}); // true


console.log({isRedFruit: isRedFruit('piña'), fruit: 'piña'}); // true

//getFruitsByColor
console.log({redFruits: getFruitsByColor('red')}); // ['manzana', 'fresa']
console.log({yellowFruits: getFruitsByColor('yellow')}); // ['piña', 'banana']
console.log({purpleFruits: getFruitsByColor('purple')}); // ['moras', 'uvas']
// console.log({ pinkFruits: getFruitsByColor('pink') }); // Error: the color must be: red, yellow, purple

// workingSteps
console.log({workingSteps: workingSteps()});

})();

Pagina: 4
PRINCIPIOS DE DRY (Don’t Repeat Yourself)

• Simplemente es evitar tener duplicidad en el código


• Simplifica las pruebas (ya que el código no se repite solo se tiene que probar en una sola
función)
• Ayuda a centralizar el proceso
• aplicar el principio DRY, usualmente lleva a re factorizar (ya que no sabemos donde se va
utilizar mas de una vez una función)

Ejemplo de DRY y NO DRY


type Size = '' | 'small' | 'medium' | 'large';

class Product {
constructor(public name: string, public price: number = 0, public size: Size = '') {
}

isProductReady(): boolean {
for (const key in this) {
switch (typeof this[key]) {
case 'string':
if (this[key].length <= 0) throw new Error(`${key} is empty`);
break;
case 'number':
if (this[key] <= 0) throw new Error(`${key} is zero`);
break;
default:
throw new Error(`${key} is invalid`);
}
}
return true;
}

toString() {
// NO DRY
if (this.name.length <= 0) throw new Error('Name is empty');
if (this.price <= 0) throw new Error('price is zero');
if (this.size.length <= 0) throw new Error('size is empty');
return `Product name: ${this.name} ${this.price} ${this.size}`;
}

toStringDRY() {
// DRY
if (!this.isProductReady()) return;
return `Product name: ${this.name} ${this.price} ${this.size}`;
}
}

(() => {
const bluePant: Product = new Product('Blue Large Pant', 100, 'large');
console.log(bluePant.toString());
console.log(bluePant.toStringDRY());
})();

CLEAN CODE EN CLASES

Pagina: 5
Al igual que lo visto en la sección de clean code para las clases se aplican las mismas reglas. y
debemos tomar en cuenta que las clases deber ser especificar para cada servicio o función que
va realizar.

PRINCIPIOS DE RESPONSABILIDAD ÚNICA

El principio de responsabilidad única o SRP (siglas del inglés, Single Responsibility Principle) en
ingeniería de software establece que cada módulo o clase debe tener responsabilidad sobre una
sola parte de la funcionalidad proporcionada por el software y esta responsabilidad debe estar
encapsulada en su totalidad por la clase. Todos sus servicios deben estar estrechamente
alineados con esa responsabilidad. Este principio está incluido en el acrónimo SOLID. Robert C.
Martín.

(() => {

// No aplicando el principio de responsabilidad única


type Gender = 'M'|'F';

class Person {
constructor(
public name: string,
public gender: Gender,
public birthdate: Date
){}
}

class User extends Person {

public lastAccess: Date;

constructor(
public email: string,
public role: string,
name: string,
gender: Gender,
birthdate: Date,
){
super( name, gender, birthdate );
this.lastAccess = new Date();
}

checkCredentials() {
return true;
}
}

class UserSettings extends User {


constructor(
public workingDirectory: string,
public lastOpenFolder : string,
email : string,
role : string,
name : string,
gender : Gender,
birthdate : Date
){
super(email, role, name, gender, birthdate );
}
}

const userSettings = new UserSettings(

Pagina: 6
'/usr/home',
'/home',
'[email protected]',
'Admin',
'Fernando',
'M',
new Date('1985-10-21')
);

console.log({ userSettings });

})();

REFACTORIZACION DEL CÓDIGO PARA RESPETAR LA RESPONSABILIDAD ÚNICA


(() => {

// Aplicando el principio de responsabilidad única


// Priorizar la composición frente a la herencia!

type Gender = 'M'|'F';

interface PersonProps {
birthdate : Date;
gender : Gender;
name : string;
}

class Person {
public birthdate: Date;
public gender : Gender;
public name : string;

constructor({ name, gender, birthdate }: PersonProps ){


this.name = name;
this.gender = gender;
this.birthdate = birthdate;
}
}

interface UserProps {
email : string;
role : string;
}

class User {

public email : string;


public lastAccess : Date;
public role : string;

constructor({
email,
role,
}: UserProps ) {
this.lastAccess = new Date();
this.email = email;
this.role = role;
}

checkCredentials() {
return true;
}
}

interface SettingsProps {
lastOpenFolder : string;

Pagina: 7
workingDirectory : string;
}

class Settings {

public workingDirectory: string;


public lastOpenFolder : string;

constructor({
lastOpenFolder,
workingDirectory,
}: SettingsProps ) {
this.lastOpenFolder = lastOpenFolder;
this.workingDirectory = workingDirectory;
}
}

interface UserSettingsProps {
birthdate : Date;
email : string;
gender : Gender;
lastOpenFolder : string;
name : string;
role : string;
workingDirectory : string;
}

class UserSettings {

public person : Person;


public user : User;
public settings: Settings;

constructor({
name, gender, birthdate,
email, role,
lastOpenFolder, workingDirectory,
}: UserSettingsProps ){

this.person = new Person({ name, gender, birthdate });


this.user = new User({ email, role });
this.settings = new Settings({ lastOpenFolder, workingDirectory })
}
}

const userSettings = new UserSettings({


birthdate: new Date('1985-10-21'),
email: '[email protected]',
gender: 'M',
lastOpenFolder: '/home',
name: 'Alexis',
role: 'Admin',
workingDirectory: '/usr/home',
});

console.log({ userSettings });

})();
Donde en el ejemplo anterior se deja responsabilidad para cada una de las clases quitando la
herencia que existía ya que la herencia es muy complicada de manejar en una responsabilidad
única para cada clase

ACRÓNIMO S.T.U.P.I.D (CODIGO QUE NO DEBES ESCRIBIR)

Pagina: 8
Seguro que la mayoría de vosotros habéis escuchado alguna vez la palabra SOLID, la Santísima
Trinidad de la programación orientada a objetos y es que bajo esta palabra se esconden sus 5
principios básicos: Single responsability, Open-closed, Liskov substitution, Interface segregation
and Dependency inversion.

Pero, si no somos SOLID… ¿qué estamos haciendo? Probablemente estés escribiendo código

STUPID! Ya te podrás imaginar que también es un acrónimo:

SINGLENTON

Uno de los primeros patrones que aprendemos por su facilidad de entender e implementar e
intenta resolver básicamente dos problemas (adiós Principio de responsabilidad única):

• Garantizar que una clase tenga una única instancia, por ejemplo, para controlar el
acceso a un recurso compartido como una base de datos
• Proporcionar un acceso seguro a estados globales (variables globales) La misma
instancia viajará por todo el código y todos los clientes la compartirán. Incluso la
mayoría de ellos no serán conscientes de que están rehusando la misma instancia
que ha sido utilizada por otros. Parece una fiesta pero definitivamente esto no mola:
• Proporciona estados globales dentro de la aplicación lo que infiere en dificultar para
probar (testing) nuestro código
• No permite inyección de dependencias
• Aplicaciones con estados globales son aplicaciones que esconden sus
dependencias
• Nuestros clientes quedan acoplados a los Singleton
• Incumple la S de SOLID. Se preocupa de solucionar dos problemas: cómo se
instancia una clase (ocultando su constructor haciéndolo privado) y cómo se
comporta su propio ciclo de vida

TIGHT COUPLING (STRONG COUPLING)

El acoplamiento fuerte es una generalización del patrón Singleton. Básicamente consiste en qué

grado tus clientes conocen detalles de las clases que consumen. Entre más detalles conozcan

(alto acoplamiento) más difícil será realizar cambios en tu código. También aumentará la

probabilidad de romper y mantener tus aplicaciones. Imagina que queremos conocer si hoy es el

cumpleaños de nuestros usuarios:

1<?php
2
3class Usuario

Pagina: 9
{
4 public int $dia_cumpleaños;
5 public int $mes_cumpleaños;
6
7 public function __construct(...): void
8
{
9
// TO-DO
10
11 }
12 }
13
14 class Cliente
15 {
16 public function felicitar(Usuario $usuario): void
17 {
18 $today = getdate();
19
20 if (
21 $today['mday'] === $usuario->dia_cumpleaños &&
22 $today['mon'] === $usuario->mes_cumpleaños
23 ){
24 // mandar email
25
}
26
27 }
}

Tenemos una clase Usuario que almacena la fecha de nacimiento del usuario como dos atributos

de clase de tipo entero: un atributo para el día y otro para el mes. Además, por otro lado, hemos

implementado una clase que mandará un e-mail de felicitación al usuario el día de su cumpleaños.

Para esto miraremos si los atributos de la clase Usuario coinciden con el día y el mes de la fecha

de hoy. El equipo A decide cambiar la forma en la que se almacena la fecha de nacimiento de un

usuario. Y nuestra clase Cliente deja de funcionar, se ha roto. ¿Por qué? Porque Cliente está

fuertemente acoplado con Usuario: conoce demasiados detalles de su implementación para

funcionar. Está violando el principio Tell, Don’t Ask: desde getters y setters hasta clases

anémicas.

• Nuestro código no es tolerante al cambio

• Dificulta reutilizar código

• Dificulta probar (testing) nuestros módulos o clases

UNTESTABILITY

Como hemos visto usar el patrón Singleton y tener código fuertemente acoplado reduce

enormemente nuestras posibilidades de probar (testing) código. Y deberíamos partir de una

Pagina: 10
premisa fundamental: ¡nuestro código debe ser fácil de probar! ¿Sabes la tranquilidad que da

llegar a producción en verde?

PREMATURE OPTIMIZATION

Seamos sinceros, la mayoría de las optimizaciones que hacemos los desarrolladores no sirven

para nada. Punto. Miento, sí, para una única cosa muy diferente a su propósito: dificultar la

legibilidad de nuestro código. Las técnicas de optimización son mucho más complejas que

sustituir un loop de 20 ítems que usa Active Record por una query hecha a pelo.

Por Internet corren las siguientes voces sobre la optimización prematura:

• No lo hagas

• No lo hagas todavía

INDESCRIPTIVE NAMING

Este parece obvio, ¿quién iba a tirarse piedras sobre su mismo tejado? Pues lo hacemos todos y

todos los días. Sobretodo evita las abreviaturas. ¿A qué debemos esta manía? Las abreviaturas

no aportan ninguna ventaja y muchas veces tiramos de ellas como solución a problemas de

indentación de código (¿sabes por qué deberías seguir una guía de estilo de código?)

Además, son peligrosas. Un ejemplo muy típico. Tmp y temp ¿esto qué significa? ¿temporal?

¿temperatura? No des por hecho que todo el mundo va utilizar tmp para temporal y temp para

temperatura.

DUPLICATION

La duplicación de código supone la violación de varios principios: Don’t Repeat Yourself


(DRY) y Keep it simple, stupid (KISS).

Aunque ojo, no en el 100% de los casos es así: si haces DDD (Domain Drive Diseng) podrías ser

bueno tener objetos repetidos en contextos diferentes; y a veces es mejor un poco de código

repetido que una mal

Pagina: 11

También podría gustarte