design-patterns - geeksforgeeks - factory design pattern c++
Las clases de utilidad son malvadas? (14)
Regla de oro
Puedes ver este problema desde dos perspectivas:
- En general
*Util
métodos de*Util
suelen ser una sugerencia de mal diseño de código o convención de nomenclatura perezosa. - Es una solución de diseño legítima para funcionalidades sin estado reutilizables entre dominios. Tenga en cuenta que para casi todos los problemas comunes existen soluciones existentes.
Ejemplo 1. Uso correcto de clases / módulos util
. Ejemplo de biblioteca externa
Supongamos que está escribiendo una aplicación que administra préstamos y tarjetas de crédito. Los datos de estos módulos se exponen a través de servicios web en formato json
. En teoría, puede convertir manualmente objetos en cadenas que estarán en json
, pero eso reinventaría la rueda. La solución correcta sería incluir en ambos módulos la biblioteca externa utilizada para convertir objetos Java al formato deseado. (en la imagen de ejemplo que he mostrado gson )
Ejemplo 2. Uso correcto de clases / módulos util
. Escribir su propia util
sin excusas a otros miembros del equipo
Como caso de uso, supongamos que tenemos que realizar algunos cálculos en dos módulos de aplicación, pero ambos necesitan saber cuándo hay días festivos en Polonia. En teoría, puede hacer estos cálculos dentro de los módulos, pero sería mejor extraer esta funcionalidad para separar el módulo.
Aquí hay detalles pequeños pero importantes. La clase / módulo que ha escrito no se llama HolidayUtil
, sino PolishHolidayCalculator
. Funcionalmente es una clase util
, pero hemos logrado evitar la palabra genérica.
Vi este hilo
Si una clase de "Utilidades" es mala, ¿dónde pongo mi código genérico?
y pensé: ¿por qué son malas las clases de utilidad?
Digamos que tengo un modelo de dominio que tiene docenas de clases de profundidad. Necesito poder instancias xml-ify. ¿Hago un método to XML en el padre? ¿Hago una clase de ayuda MyDomainXmlUtility.toXml? Este es un caso en el que la necesidad comercial abarca todo el modelo de dominio: ¿realmente pertenece como un método de instancia? ¿Qué pasa si hay un montón de métodos auxiliares en la funcionalidad xml de la aplicación?
Con Java 8 puedes usar métodos estáticos en las interfaces ... problema resuelto.
Creo que el consenso general es que las clases de utilidad no son malas per se . Solo necesitas usarlos juiciosamente:
Diseñe los métodos de utilidad estáticos para que sean generales y reutilizables. Asegúrate de que sean apátridas; es decir, no hay variables estáticas.
Si tiene muchos métodos de utilidad, divídalos en clases de una manera que facilite a los desarrolladores encontrarlos.
No use clases de utilidad donde los métodos estáticos o de instancia en una clase de dominio sean una mejor solución. Por ejemplo, considere si los métodos en una clase base abstracta o una clase de ayuda instantánea serían una mejor solución.
Para Java 8 en adelante, los "métodos predeterminados" en una interfaz pueden ser una mejor opción que las clases de utilidad.
La otra forma de ver esta pregunta es observar que en la pregunta citada, "Si las clases de utilidad son" malvadas ", es un argumento de tipo " hombre de paja ". Es como si yo preguntara:
"Si los cerdos pueden volar, ¿debería llevar un paraguas?".
En la pregunta anterior, en realidad no estoy diciendo que los cerdos puedan volar ... o que estoy de acuerdo con la proposición de que podrían volar.
Las declaraciones típicas de "xyz es malo" son dispositivos retóricos que intentan hacerle pensar al plantear un punto de vista extremo. Raramente (si es que alguna vez) tienen la intención de ser declaraciones de hechos literales.
Cuando no puedo agregar un método a una clase (por ejemplo, la Account
está bloqueada frente a los cambios realizados por los desarrolladores Jr.), simplemente agrego algunos métodos estáticos a mi clase de utilidades de esta manera:
public static int method01_Account(Object o, String... args) {
Account acc = (Account)o;
...
return acc.getInt();
}
Es muy fácil marcar algo como una utilidad simplemente porque el diseñador no puede pensar en un lugar apropiado para poner el código. A menudo hay pocas "utilidades" verdaderas.
Como regla general, suelo conservar el código en el paquete en el que se usa por primera vez, y luego solo refactorizarlo a un lugar más genérico si descubro que más adelante realmente es necesario en otro lugar. La única excepción es si ya tengo un paquete que realiza funciones similares o relacionadas, y el código se ajusta mejor allí.
La mayoría de las clases de Util son malas porque:
- Ellos amplían el alcance de los métodos. Hacen público el código que de otro modo sería privado. Si el método util es estable (es decir, no necesita actualización) es mejor, en mi opinión, copiar y pegar métodos de ayuda privada que exponerlo como API y dificultar la comprensión de cuál es el punto de entrada público a un componente ( mantener un árbol estructurado llamado jerarquía con un padre por método que es más fácil segregar mentalmente en componentes, lo que es más difícil cuando se tienen métodos llamados desde múltiples métodos principales).
- Dan como resultado un código muerto. Con el tiempo, los métodos Util no se utilizan a medida que su aplicación evoluciona y usted termina con un código no utilizado que contamina su base de códigos. Si hubiera seguido siendo privado, su compilador le diría que el método no está siendo utilizado y que usted podría simplemente eliminarlo (el mejor código es ningún código).
Hay algunas analogías para bibliotecas estáticas vs dinámicas.
Las clases de utilidad no son exactamente malas, pero pueden violar los principios que componen un buen diseño orientado a objetos. En un buen diseño orientado a objetos, la mayoría de las clases deberían representar una sola cosa y todos sus atributos y operaciones. Si estás operando en una cosa, ese método probablemente sea miembro de esa cosa.
Sin embargo, hay ocasiones en las que puede usar clases de utilidad para agrupar varios métodos, por ejemplo, la clase java.util.Collections
, que proporciona varias utilidades que se pueden usar en cualquier colección de Java. Estos no son específicos de un tipo particular de Colección, sino que implementan algoritmos que se pueden usar en cualquier Colección.
En realidad, lo que debe hacer es pensar en su diseño y determinar dónde tiene más sentido poner los métodos. Por lo general, es como operaciones dentro de una clase. Sin embargo, a veces, es de hecho como una clase de utilidad. Sin embargo, cuando utilice una clase de utilidad, no solo aplique métodos aleatorios, sino que organice los métodos según el propósito y la funcionalidad.
Las clases de utilidad no son siempre malvadas. Pero solo deberían contener los métodos que son comunes en una amplia gama de funcionalidades. Si hay métodos que solo se pueden usar entre un número limitado de clases, considere la posibilidad de crear una clase abstracta como un elemento primario común y poner los métodos en ella.
Las clases de utilidad que contienen métodos estáticos sin estado pueden ser útiles. Estos son a menudo muy fáciles de probar por unidad.
Las clases de utilidad son malas porque significan que eras demasiado perezoso para inventar un mejor nombre para la clase :)
Dicho eso, soy flojo. A veces solo necesitas hacer el trabajo y tienes la mente en blanco ... es cuando las clases de "utilidad" empiezan a entrar.
Las clases de utilidad son problemáticas porque no agrupan las responsabilidades con los datos que las respaldan.
Sin embargo, son extremadamente útiles y los construyo todo el tiempo como estructuras permanentes o como escalones durante un refactor más completo.
Desde una perspectiva de código limpio , las clases de utilidad infringen la responsabilidad única y el principio de abierto cerrado. Tienen muchas razones para cambiar y, por diseño, no son extensibles. Realmente deberían existir solo durante la refactorización como cruft intermedio.
Mirando hacia atrás a esta pregunta ahora, diría que los métodos de extensión de C # destruyen completamente la necesidad de clases de utilidad. Pero no todos los lenguajes tienen una construcción tan genial.
También tiene JavaScript, donde puede agregar una nueva función directamente al objeto existente.
Pero no estoy seguro de que realmente haya una manera elegante de resolver este problema en un lenguaje más antiguo como C ++ ...
El buen código OO es un poco difícil de escribir, y es difícil de encontrar, ya que escribir Good OO requiere mucho más tiempo / conocimiento que escribir un código funcional decente.
Y cuando tienes un presupuesto ajustado, a tu jefe no siempre le agrada ver que te has pasado todo el día escribiendo un montón de clases ...
No estoy totalmente de acuerdo en que las clases de utilidad sean malas.
Si bien una clase de utilidad puede violar los principios OO de alguna manera, no siempre son malas.
Por ejemplo, imagine que desea una función que limpie una cadena de todas las subcadenas que coincidan con el valor x.
stl c ++ (a partir de ahora) no es compatible directamente con esto.
Puede crear una extensión polimórfica de std :: string.
Pero el problema es, ¿realmente quieres que CADA cadena que usas en tu proyecto sea tu clase de cadena extendida?
Hay momentos en que OO realmente no tiene sentido, y este es uno de ellos. Queremos que nuestro programa sea compatible con otros programas, por lo que nos quedaremos con std :: string y crearemos una clase StringUtil_ (o algo así).
Yo diría que es mejor si te quedas con una utilidad por clase. Yo diría que es una tontería tener un util para todas las clases o muchos utils para una clase.
Supongo que comienza a volverse malvado
1) Se vuelve demasiado grande (solo agrúpalos en categorías significativas en este caso).
2) Métodos que no deberían ser métodos estáticos están presentes
Pero mientras no se cumplan estas condiciones, creo que son muy útiles.