Dado este código ficticio, que tiene una declaración if con un código detallado (imagínese que esta es una declaración if realmente detallada), decido crear una función util para que mi código sea más limpio. Cuando elijo crear una función de utilidad, ¿debería implementarse como una interfaz o puedo implementar la función concreta en su lugar? Puedo usar esta misma función nuevamente en otra clase.
Código ficticio:
export class UserRepository { constructor (private readonly database: DatabaseProtocol) {} async findUserByEmail (data: Record<string, any>): Promise<IUser | null> { const userCollection = this.database.collection('users') if (Object.keys(data).length === 0) return null const user = await userCollection.findOne({ email: data.email }) return user } }Implementación concreta:
export class UserRepository { constructor (private readonly database: DatabaseProtocol) {} async findUserByEmail (data: Record<string, any>): Promise<IUser | null> { const userCollection = this.database.collection('users') if (isEmpty(data)) return null const user = await userCollection.findOne({ email: data.email }) return user } }Implementación de la interfaz:
export class UserRepository { constructor ( private readonly database: DatabaseProtocol, private readonly isEmpty: IsEmptyProtocol ) {} async findUserByEmail (data: Record<string, any>): Promise<IUser | null> { const userCollection = this.database.collection('users') if (this.isEmpty(data)) return null const user = await userCollection.findOne({ email: data.email }) return user } }Método privado:
export class UserRepository { constructor ( private readonly database: DatabaseProtocol ) {} async findUserByEmail (data: Record<string, any>): Promise<IUser | null> { const userCollection = this.database.collection('users') if (this.isEmpty(data)) return null const user = await userCollection.findOne({ email: data.email }) return user } isEmpty (data: Record<string, any>): boolean { return Object.keys(data).length === 0 } }¿Qué opción anterior se consideraría la mejor práctica?
Realmente no hay ninguna implementación alternativa que desee inyectar para tales funciones de ayuda, por lo tanto, una interfaz sería excesiva y engorrosa de usar.
Si observa Java, generalmente tienen funciones de utilidad en clases plurales como Arrays , donde podemos encontrar utilidades como Arrays#asList . JavaScript hace lo mismo con, por ejemplo, Object.entries .
Con los módulos, solo podría tener un módulo objects.ts y exportar una función isEmpty para reutilizar.
Sin embargo, una cosa a tener en cuenta es si "vacío" podría tener diferentes significados. Por ejemplo, tal vez "vacío" en otro contexto podría significar que podría haber claves, pero no cuentan si hacen referencia a null o undefined . Por lo tanto, es importante documentar el comportamiento correctamente.
Además, el cheque vacío es realmente específico del dominio. Parece representar en su caso si hay o no un filtro de correo electrónico presente. Por lo tanto, en lugar de llamar a isEmpty directamente, lo más probable es que tenga una función privada hasEmailCriteria o algo así.
Sin embargo, al final también creo que la firma de findUserByEmail es bastante mala. ¿Por qué no simplemente aceptar un correo electrónico como findUserByEmail(email: string) en su lugar? O incluso mejor, tal vez findUserByEmail(address: EmailAddress) .
Aquí están mis 2 centavos:
IsEmpty es necesaria solo para la clase UserRepository , conviértala en un método privado de esa clase.IsEmpty , entonces vaya con la Interface implementation: solución que propuso en su pregunta.IsEmpty se usa en todas sus clases (o en la gran mayoría), conviértala en una clase global con métodos estáticos. Lo sé, lo sé, el tío Bob no permite el estado global (y con razón), pero esta es una excepción. Este caso es una preocupación transversal (consulte mi respuesta aquí para obtener más detalles).