Respuesta tardía pero no puedo resistirme.
¿Es X la mayoría de las clases en Y buenas o un antipatrón?
En la mayoría de los casos, la mayoría de las reglas, aplicadas sin pensar, irán terriblemente mal (incluida esta).
Permíteme contarte una historia sobre el nacimiento de un objeto en medio del caos de un código de procedimiento correcto, rápido y sucio que ocurrió, no por diseño, sino por desesperación.
Mi pasante y yo estamos programando pares para crear rápidamente un código desechable para raspar una página web. No tenemos absolutamente ninguna razón para esperar que este código dure mucho, por lo que solo estamos eliminando algo que funciona. Tomamos toda la página como una cadena y cortamos las cosas que necesitamos de la manera más increíblemente frágil que puedas imaginar. No juzgues Funciona.
Ahora, mientras hacía esto, creé algunos métodos estáticos para cortar. Mi pasante creó una clase de DTO que se parecía mucho a la tuya CatData
.
Cuando vi por primera vez el DTO me molestó. Los años de daño que Java ha causado en mi cerebro me hicieron retroceder en los campos públicos. Pero estábamos trabajando en C #. C # no necesita getters y setters prematuros para preservar su derecho a hacer que los datos sean inmutables o encapsulados más tarde. Sin cambiar la interfaz, puede agregarlos cuando lo desee. Tal vez solo para poder establecer un punto de interrupción. Todo sin decirles nada a sus clientes. Sí C #. Boo Java.
Así que me contuve la lengua. Vi como él usaba mis métodos estáticos para inicializar esto antes de usarlo. Teníamos unos 14 de ellos. Era feo, pero no teníamos motivos para preocuparnos.
Luego lo necesitábamos en otros lugares. Nos encontramos queriendo copiar y pegar el código. Se lanzaron 14 líneas de inicialización. Estaba empezando a ser doloroso. Dudó y me pidió ideas.
De mala gana le pregunté, "¿considerarías un objeto?"
Volvió a mirar su DTO y frunció el ceño confundido. "Es un objeto".
"Me refiero a un objeto real"
"¿Eh?"
"Déjame mostrarte algo. Tú decides si es útil"
Elegí un nuevo nombre y rápidamente preparé algo que se parecía un poco a esto:
public class Cat{
CatData(string catPage) {
this.catPage = catPage
}
private readonly string catPage;
public string name() { return chop("name prefix", "name suffix"); }
public string weight() { return chop("weight prefix", "weight suffix"); }
public string image() { return chop("image prefix", "image suffix"); }
private string chop(string prefix, string suffix) {
int start = catPage.indexOf(prefix) + prefix.Length;
int end = catPage.indexOf(suffix);
int length = end - start;
return catPage.Substring(start, length);
}
}
Esto no hizo nada que los métodos estáticos no estuvieran haciendo. Pero ahora había absorbido los 14 métodos estáticos en una clase donde podían estar solos con los datos en los que trabajaban.
No forcé a mi interno a usarlo. Simplemente lo ofrecí y le dejé decidir si quería seguir con los métodos estáticos. Me fui a casa pensando que probablemente se apegaría a lo que ya tenía trabajando. Al día siguiente descubrí que lo estaba usando en muchos lugares. Descomprimió el resto del código que todavía era feo y procesal, pero esta parte de complejidad ahora estaba oculta para nosotros detrás de un objeto. Fue un poco mejor.
Ahora, seguro, cada vez que acceda a esto, está haciendo un buen trabajo. Un DTO es un buen valor rápido en caché. Me preocupé por eso, pero me di cuenta de que podía agregar el almacenamiento en caché si alguna vez lo necesitáramos sin tocar ninguno de los códigos de uso. Así que no me voy a molestar hasta que nos importe.
¿Estoy diciendo que siempre debes apegarte a los objetos OO sobre los DTO? No. El DTO brilla cuando necesita cruzar un límite que le impide moverse. Los DTO tienen su lugar.
Pero también lo hacen los objetos OO. Aprende a usar ambas herramientas. Aprende lo que cuesta cada uno. Aprenda a dejar que el problema, la situación y el interno decidan. Dogma no es tu amigo aquí.
Dado que mi respuesta ya es ridículamente larga, permítame desacreditarle algunos conceptos erróneos con una revisión de su código.
Por ejemplo, una clase generalmente tiene miembros y métodos de clase, por ejemplo:
public class Cat{
private String name;
private int weight;
private Image image;
public void printInfo(){
System.out.println("Name:"+this.name+",weight:"+this.weight);
}
public void draw(){
//some draw code which uses this.image
}
}
¿Dónde está tu constructor? Esto no me muestra lo suficiente como para saber si es útil.
Pero después de leer sobre el principio de responsabilidad única y el principio de apertura cerrada, prefiero separar una clase en DTO y clase auxiliar con métodos estáticos solamente, por ejemplo:
public class CatData{
public String name;
public int weight;
public Image image;
}
public class CatMethods{
public static void printInfo(Cat cat){
System.out.println("Name:"+cat.name+",weight:"+cat.weight);
}
public static void draw(Cat cat){
//some draw code which uses cat.image
}
}
Creo que se ajusta al principio de responsabilidad única porque ahora la responsabilidad de CatData es mantener solo los datos, no le importan los métodos (también para CatMethods).
Puede hacer muchas cosas tontas en nombre del Principio de responsabilidad única. Podría argumentar que las cadenas de gato y las entradas de gato deben estar separadas. Que los métodos de dibujo y las imágenes deben tener su propia clase. Que su programa en ejecución es una responsabilidad única, por lo que solo debe tener una clase. :PAG
Para mí, la mejor manera de seguir el Principio de responsabilidad única es encontrar una buena abstracción que le permita poner la complejidad en una caja para que pueda ocultarla. Si puede darle un buen nombre que evite que las personas se sorprendan por lo que encuentran cuando miran dentro, lo ha seguido bastante bien. Esperar que dicte más decisiones que eso es pedir problemas. Honestamente, ambos listados de código lo hacen, así que no veo por qué SRP importa aquí.
Y también se ajusta al principio abierto cerrado porque agregar nuevos métodos no necesita cambiar la clase CatData.
Bueno no. El principio de abrir y cerrar no se trata de agregar nuevos métodos. Se trata de poder cambiar la implementación de métodos antiguos y no editar nada. Nada que te use y no tus viejos métodos. En su lugar, escribe un código nuevo en otro lugar. Alguna forma de polimorfismo lo hará muy bien. No veo eso aquí.
Mi pregunta es, ¿es un buen o un antipatrón?
Bueno demonios, ¿cómo debería saberlo? Mire, hacerlo de cualquier manera tiene beneficios y costos. Cuando separa el código de los datos, puede cambiarlos sin tener que volver a compilar el otro. Tal vez eso es críticamente importante para ti. Tal vez solo haga que su código sea innecesariamente complicado.
Si te hace sentir mejor, no estás tan lejos de algo que Martin Fowler llama un objeto de parámetro . No tiene que tomar solo primitivas en su objeto.
Lo que me gustaría que hicieras es desarrollar una idea de cómo hacer tu separación, o no, en cualquier estilo de codificación. Porque lo creas o no, no estás obligado a elegir un estilo. Solo tienes que vivir con tu elección.