Generics en clases
Clase 22 de 25 • Curso de TypeScript: Programación Orientada a Objetos y Asincronismo
Contenido del curso
Clase 22 de 25 • Curso de TypeScript: Programación Orientada a Objetos y Asincronismo
Contenido del curso
Carlos Eduardo Bracho Ramírez
Jaime Eduardo Falla Cardozo
Angel Aponte
Gabriel Hernández Valdez
Celso Espinoza
Alex Guaman
Jose Fabio Ortega Estrada
Odannys De la Cruz
David Higuera
Jhorman Salazar
Diego Raciel Ortega Hernandez
Ronaldo Delgado
Kevin Candia
carlos orozco
Felix Torres Javier Antonio
Andre Huaman Yovera
Andre Huaman Yovera
Frank Yupanqui
Brahyan Antonio Martinez Madera
Raycris Maldonado
Andrés Felipe Eslava Zuluaga
Miguel Angel Reyes Moreno
Literal pasé las 2 clases preguntándome por qué esto era importante, hasta que la luz salvadora me iluminó en un instante de raciocinio, y comprendí la magnitud de Generics. Sencillamente, wow.
¿Esto serviría para crear un Backend completo con Node? Si alguien tiene un ejemplo, se lo agradecería.
Realmente los genéricos nos puede ahorrar muchas líneas de código, son super poderosos si se implementan bien.
Imagina que tienes funciones que representa el CRUD de tu aplicación, pero puedes tener n cruds por cada módulo. Ahora teniendo esto en cuenta puedes reutilizar las funciones cambiando simplemente su alcance(entidad). Por ejemplo en tu aplicación tienes módulos para crear usuarios y productos. Que pasaría si los métodos de ambos módulos(los servicios) hicieran lo mismo?, aplicando esto puedes abstraer/separar lógica y reutilizarla para el orm siendo que los métodos siempre van a hacer lo mismo lo que cambia es tu entity.
Mi cerebro exploto, pero entendi muchas cosas con esta clase :D
Vengo usando JS para el backend pero con esto pues no hay pierde. Me cambio a usar TS desde ahora. jeje
Tambien se puede escribir usando la convencion de nombres para genericos, que es comunmente con lo que te vas a topar
export class BaseHttpService<T> { constructor( private url: string ){} async getAll(): Promise<T[]> { const { data } = await axios.get<T[]>(this.url); return data; } }
Creo entender, usando la misma clase se hacen diferentes peticiones al servidor de diferentes entidades, lo que cambiaría es el tipado o genérico que le apliquemos a dicha clase, con esto se ahorraría la generación de una clase para cada entidad y varios métodos crud para cada entidad, lo cual ahorraría mucho en materia de líneas de código. Pero me surge una pregunta: ¿Hasta qué punto esto es beneficioso, ya que separar la lógica aparte de tener mayor claridad conserva el principio de una sola responsabilidad .? Si por alguna razon esta clase falla que sería la unica, fallaría toda la estructura relacionada con esa clase, por el contrario separando la lógica en diferentes clases mitigaría de cierta forma algún error que se presente en alguna clase especifica. Por otro lado las clases son representaciones abstractas del mundo real por ende cada entidad del mundo real, se representa como una clase por separado, entonces, se podría decir que separar las clases es algo mas cercano al mundo real. Entonces deben haber casos específicos donde se usen este tipo de genéricos . O NO, NO SE. solo son conjeturas mías, no soy experto. alguien que sepa mas que yo que me oriente por favor.
Estás en lo correcto, es buena práctica tener separado tus servicios por cada entidad.
Sin embargo, debes analizar tu caso específico. Si es algo pequeño entonces un genérico te ayudará mucho. Para proyecto grandes no se recomienda abusar de estos, no por la modularidad si no por mantenibilidad y por la extensibilidad.
Por otro lado los genéricos se crearon para poder tipar código reutilizable sin necesidad de usar el tipo any o tener que especificar todos los tipos que tendrá dicho código reutilizable.
Puedes ver mas aquí
También se puede capturar el resultado de una promesa de este modo, para no hacer la función auto-invocada:
const url1 = "https://api.escuelajs.co/api/v1/products"; const productService = new BaseHttpService<Product>(url1); productService.getAll().then(console.log);
Este curso esta genial, Typescript es muy poderoso!. Ánimo chicos, no paremos de aprender!.
Llevo dos horas intentando entender los genéricos. Hasta que me carburo el cerebro y por arte de magia lo entendí :0
Los genéricos son lo mejor
Sería mucho mejor pasar la url en el método, y no el constructor, ya que si transformamos nuestro servicio a un singleton, la segunda instanacia del servicio, la url no la tomaría.
🤯🤯🤯
Parece que ya no sirve la api :/
Aquí tienes un ejemplo de cómo implementar el patrón Singleton en TypeScript para la clase BaseHttpService:
import axios from 'axios'; export class BaseHttpService<TypeClass> { private static instance: BaseHttpService<any>; private url: string; private constructor(url: string) { this.url = url; } public static getInstance<TypeClass>(url: string): BaseHttpService<TypeClass> { if (!BaseHttpService.instance) { BaseHttpService.instance = new BaseHttpService<TypeClass>(url); } return BaseHttpService.instance; } async getAll() { const { data } = await axios.get<TypeClass[]>(this.url); return data; } async update(id: string, changes: Partial<TypeClass>) { const { data } = await axios.put(`${this.url}/${id}`, changes); return data; } async create(dto: TypeClass) { const { data } = await axios.post(this.url, dto); return data; } async findOne(id: string) { const { data } = await axios.get(`${this.url}/${id}`); return data; } async delete(id: string) { const { data } = await axios.delete(`${this.url}/${id}`); return data; } }
En este ejemplo, hemos agregado un método estático llamado getInstance que se encarga de crear y mantener una única instancia de la clase BaseHttpService. El constructor de la clase es privado, lo que significa que no se puede acceder directamente a él desde fuera de la clase. Esto evita que se creen múltiples instancias de BaseHttpService y garantiza que siempre se utilice la misma instancia en todo el código.
.
Ahora, veamos por qué es bueno utilizar el patrón Singleton:
Garantiza una única instancia: El patrón Singleton asegura que solo haya una instancia de una clase determinada en todo el programa. Esto puede ser útil cuando se necesita un objeto único para coordinar acciones en diferentes partes del código.
Acceso global: Al utilizar el patrón Singleton, la instancia única se vuelve accesible globalmente desde cualquier parte del programa. Esto simplifica el acceso a la funcionalidad proporcionada por la clase, ya que no es necesario pasar la instancia entre diferentes componentes o clases.
Mantiene el estado: Si la clase Singleton tiene estado, este estado se mantiene a lo largo de todo el ciclo de vida de la aplicación. Esto puede ser útil cuando se necesita mantener información o configuraciones específicas en un solo lugar y compartirlas en diferentes partes del programa.
Promueve el acoplamiento débil: Al utilizar el patrón Singleton, los componentes o clases que dependen de la instancia no necesitan conocer los detalles de cómo se crea o se mantiene esa instancia. Solo necesitan solicitar la instancia al Singleton, lo que promueve un acoplamiento débil y facilita la modificación o sustitución de la implementación del Singleton en el futuro.
. Sin embargo, es importante tener en cuenta que el patrón Singleton también puede tener algunas desventajas. Por ejemplo, puede dificultar las pruebas unitarias, ya que la dependencia de la instancia única puede ser difícil de simular o reemplazar en las pruebas. Además, el uso excesivo del patrón Singleton puede ocultar dependencias y dificultar la comprensión del flujo de datos y la arquitectura general del programa. . Por lo tanto, es recomendable utilizar el patrón Singleton con moderación y evaluar cuidadosamente si es la mejor solución para el problema específico que se está abordando.
Si deseas utilizar el patrón Singleton en tu código, puedes modificarlo de la siguiente manera:
(async () => { const url1 = 'https://api.escuelajs.co/api/v1/products'; const productService = BaseHttpService.getInstance<Product>(url1); const rta = await productService.getAll(); console.log('products', rta.length); const url2 = 'https://api.escuelajs.co/api/v1/categories'; const categoryService = BaseHttpService.getInstance<Category>(url2); const rta1 = await categoryService.findOne('2'); console.log('category', rta1); })();
En este caso, en lugar de crear nuevas instancias de BaseHttpService utilizando el operador new, utilizamos el método estático getInstance para obtener la instancia única de la clase. Esto asegura que siempre estemos utilizando la misma instancia de BaseHttpService en todo el código.
.
Ten en cuenta que en este ejemplo se está utilizando el método getInstance con diferentes URL y tipos de clase (Product y Category), lo cual está permitido debido a la implementación genérica de la clase BaseHttpService. Cada llamada a getInstance con una URL diferente creará una nueva instancia de BaseHttpService asociada a esa URL. Sin embargo, si se realiza una llamada adicional a getInstance con la misma URL, se obtendrá la misma instancia previamente creada.
.
De esta manera, el patrón Singleton garantiza que siempre estemos trabajando con una única instancia de BaseHttpService para cada URL, lo cual puede ser beneficioso para compartir el estado y la funcionalidad de la clase en diferentes partes del código.
Personalizando la clase en execution time.
Typescript the best💙
Tuve que mirar la clases dos veces para poder entender un poco más acerda de la clases generics pero aquí está el resumen que pude sacar
++RESUMEN:++ Este es un servivio generico que podemos utilizar solamente pasando el tipo de dato que queremos utulizar o hacer request, eso nos ahorraria hacer un servicio para productos, categorias, usuarios etc...
Mi aporte:
import axios from 'axios'; import { Category } from '../models/category.model'; import { Product } from '../models/product.model'; export class BaseHttpService<TypeClass> { // data: TypeClass[] = []; constructor (private url: string) {} async getAll(): Promise<TypeClass[]> { const { data } = await axios.get<TypeClass[]>(this.url); return data; } } (async () => { const URL_PRODUCTS = 'https://api.escuelajs.co/api/v1/products' const URL_CATEGORIES = 'https://api.escuelajs.co/api/v1/categories' const productService = new BaseHttpService<Product>(URL_PRODUCTS); const categoryService = new BaseHttpService<Category>(URL_CATEGORIES); const products = await productService.getAll() const categories = await categoryService.getAll() console.log('products: >>>> ', products.length); console.log('categories: >>>> ', categories.length); })()
Generics en clases
import axios from "axios"; import { Category } from "./models/category.model"; import { Product } from "./models/product.model"; export class BaseHttpService<TypeClass> { constructor( private url: string, ) { } async getAll() { const { data } = await axios.get<TypeClass[]>(this.url) return data; } } (async()=> { const url1 = 'https://api.escuelajs.co/api/v1/products' const productService = new BaseHttpService<Product>(url1) const rta = await productService.getAll() console.log('products: ', rta.length) const url2 = 'https://api.escuelajs.co/api/v1/categories' const categoryService = new BaseHttpService<Category>(url2) const rta2 = await categoryService.getAll() console.log('categories: ', rta2.length) })()