2009-10-31 7 views
6

Nunca seguro dónde colocar funciones como:Creando una clase de AppToolbox de Catch-All: ¿es esta una mala práctica?

String PrettyPhone(String phoneNumber) // return formatted (999) 999-9999 
String EscapeInput(String inputString) // gets rid of SQL-escapes like ' 

puedo crear una clase Toolbox para cada aplicación que sirve como depósito para las funciones que no se ajustan correctamente a otra clase. He leído que tales clases son malas prácticas de programación, específicamente mal diseño orientado a objetos. Sin embargo, dichas referencias parecen más la opinión de diseñadores y desarrolladores individuales que un consenso general. Entonces mi pregunta es, ¿es una caja de herramientas para todos los bolsillos un patrón de diseño pobre? Si es así, ¿por qué y qué alternativa hay?

Respuesta

6

Gran pregunta. Siempre encuentro que cualquier proyecto suficientemente complejo requiere clases de "utilidad". Creo que esto es simplemente porque la naturaleza de la programación orientada a objetos nos obliga a ubicar las cosas en una taxonomía jerárquica claramente estructurada, cuando esto no siempre es factible o apropiado (por ejemplo, intentar crear un modelo de objetos para mamíferos y luego exprimir al ornitorrinco en) Este es el problema que motiva el trabajo en aspect oriented programming (c.f. cross cutting concern). A menudo, lo que entra en una clase de utilidad son cuestiones transversales.

Una alternativa al uso de la caja de herramientas o las clases de utilidad es utilizar métodos de extensión para proporcionar funcionalidad adicional necesaria a los tipos primitivos. Sin embargo, el jurado todavía está deliberando sobre si eso constituye un buen diseño de software.

Mi última palabra sobre el tema es: ve con ella si lo necesitas, solo asegúrate de que no estés cortando mejores diseños. Por supuesto, siempre puedes refactorizar más adelante si lo necesitas.

1

No hay nada de malo en esto. Una cosa es tratar de dividirlo en partes lógicas. Al hacer esto, puedes mantener tu intellisense limpio.

MyCore.Extensions.Formatting.People 
MyCore.Extensions.Formatting.Xml 
MyCore.Extensions.Formatting.Html 
0

Creo que la razón está mal visto es debido a que la "caja de herramientas" puede crecer y se le carga de una tonelada de recursos cada vez que desea llamar una sola función.

También es más elegante tener los métodos que se aplican a los objetos en la clase real, simplemente tiene más sentido.

Dicho esto, personalmente no creo que sea un problema, pero lo evitaría simplemente por las razones anteriores.

2

En estos ejemplos estaría más inclinado a extender la cadena:

class PhoneNumber extends String 
{ 
    public override string ToString() 
    { 
     // return (999) 999-9999 
    } 
} 

Si anota todos los lugares que necesita estas funciones se puede averiguar lo que realmente lo usa y luego añadirlo a la clase apropiada . A veces puede ser difícil, pero aún así es algo a lo que debes aspirar.

EDIT:

Como se señala más adelante, no se puede anular cadena en C#. El punto que estaba tratando de hacer es que esta operación se realiza sobre un número de teléfono de modo que es donde la función pertenece:

interface PhoneNumber 
{ 
    string Formatted(); 
} 

Si tiene diferentes formatos que se pueden intercambiar las implementaciones de Fax y sin ensuciar el código con sentencias if , p.ej,

En lugar de:

if(country == Countries.UK) output = Toolbox.PhoneNumberUK(phoneNumber); 
else ph = Toolbox.PhoneNumberUS(phoneNumber); 

sólo puede utilizar:

output = phoneNumber.Formatted(); 
+0

La pregunta es sobre C#, no sobre Java. De todos modos, la clase String está sellada (final en Java), tanto en .NET como en Java –

2

creo una clase auxiliar estática es la primera cosa que viene a la mente. Es tan común que algunos incluso se refieren a él como parte del diseño orientado a objetos. Sin embargo, el mayor problema con las clases de ayuda es que tienden a convertirse en un gran basurero. Creo que vi esto suceder en algunos de los proyectos más grandes en los que participé. Estás trabajando en una clase y no sabes dónde colocar esta y esa función, así que la pones en tu clase de ayuda. En ese momento, tus ayudantes no se comunican bien sobre lo que hacen. El nombre 'helper' o 'util' en sí mismo en el nombre de la clase no significa nada. Creo que casi todos los gurús OO abogan en contra de los ayudantes ya que puedes reemplazarlos fácilmente con clases más descriptivas si le das suficiente atención. Tiendo a estar de acuerdo con este enfoque, ya que creo que los ayudantes violan el principio de responsabilidad única. Honestamente, toma esto con un grano de sal. Estoy un poco obstinado en OOP :)

+0

. Tiendo a crear ayudantes específicos en una biblioteca común. Por lo tanto, no verá una clase auxiliar general que contenga tanto funciones auxiliares IO como ayudantes de cadenas. Pero sí tengo: IoHelper, StringHelper, etc. Así es como obtengo mi 'separación de preocupaciones'. ;-) –

1

Mi experiencia ha sido que las funciones de utilidad rara vez se producen de forma aislada. Si necesita un método para formatear los números de teléfono, también necesitará uno para validar los números de teléfono y analizar los números de teléfono. Siguiendo el principio YAGNI, ciertamente no querrás escribir esas cosas hasta que realmente las necesites, pero creo que es útil seguir adelante y separar esa funcionalidad en clases individuales. El crecimiento de esas clases de métodos únicos en subsistemas menores ocurrirá naturalmente a lo largo del tiempo. He encontrado que esta es la forma más fácil de mantener el código organizado, comprensible y mantenible a largo plazo.

1

Cuando creo una aplicación, normalmente creo una clase estática que contiene métodos y propiedades estáticas que no puedo encontrar en otro sitio.

No es un diseño especialmente bueno, pero ese es el punto: me da un lugar para localizar toda una clase de decisiones de diseño que aún no he pensado. En general, a medida que la aplicación crece y se perfecciona a través de la refactorización, se vuelve más claro dónde deberían residir realmente estos métodos y propiedades. Afortunadamente, el estado de las herramientas de refactorización es tal que esos cambios generalmente no son excepcionalmente dolorosos de realizar.

He intentado hacerlo de otra forma, pero a la inversa está implementando un modelo de objetos antes de saber lo suficiente sobre mi aplicación para diseñar el modelo de objetos correctamente. Si hago que, gasto una buena cantidad de tiempo y energía con una solución mediocre que tengo que revisar y reconstruir desde cero en algún momento en el futuro. Bien, está bien, si sé que voy a refaccionar este código, ¿qué tal si me salte el paso de diseñar y construir las clases innecesariamente complicadas que realmente no funcionan?

Por ejemplo, he creado una aplicación que está siendo utilizada por varios clientes. Descubrí desde el principio que necesitaba una forma de separar los métodos que deben funcionar de forma diferente para diferentes clientes. Creé un método de utilidad estático al que podía llamar en cualquier punto del programa donde necesitaba llamar a un método personalizado, y lo atrapé en mi clase estática.

Esto funcionó bien durante meses. Pero llegó un momento en que empezaba a parecer feo. Y entonces decidí refactorizarlo en su propia clase.Y mientras revisaba mi código mirando todos los lugares donde se llamaba este método, quedó muy claro que todos los métodos personalizados realmente necesitaban ser miembros de una clase abstracta, los ensamblajes de los clientes debían contener una única clase derivada que implementa todos los métodos abstractos, y luego el programa solo necesitaba sacar el nombre del ensamblaje y el espacio de nombres de su configuración y crear una instancia de la clase de características personalizadas al inicio. Fue muy sencillo para mí encontrar todos los métodos que tenían que personalizarse, ya que todo lo que tenía que hacer era encontrar todos los lugares donde se llamaba a mi método de cargar una característica personalizada. Me llevó casi toda la tarde recorrer toda la base de código y racionalizar este diseño, y el resultado final es realmente flexible y robusto y resuelve el problema correcto.

La cuestión es que cuando implementé ese método por primera vez (en realidad eran tres o cuatro métodos interrelacionados), reconocí que no era la respuesta correcta. Pero no sabía lo suficiente como para decidir cuál era la respuesta correcta. Así que fui con la respuesta incorrecta más simple hasta que la respuesta correcta fue clara.

0

Publiqué un comentario, pero pensé que elaboraría un poco más.

Lo que hago es crear una biblioteca común con espacios de nombres: [Organización]. [Producto] .Common como el raíz y un sub espacio de nombres Ayudantes.

Algunas personas aquí mencionan cosas como crear una clase y empujar algunas cosas que no saben dónde más poner allí. Incorrecto. Yo diría que, incluso si necesitas un método de ayuda, está relacionado con algo, así que crea una clase de ayuda estática adecuadamente nombrada (IoHelper, StringHelper, etc.) y colócala en el espacio de nombres de los Ayudantes. De esa forma, obtienes cierta estructura y obtienes algún tipo de separación de preocupaciones.

En el espacio de nombres raíz, puede usar clases de utilidad de instancia que requieren el estado (¡existen!). Y no hace falta decir que también use un nombre de clase apropiado, pero no sufijo con Helper.

Cuestiones relacionadas