Réponse tardive mais je ne résiste pas.
Est-ce que la plupart des classes en Y sont bonnes ou anti-modèle?
Dans la plupart des cas, la plupart des règles, appliquées sans réfléchir, vont pour la plupart tourner horriblement mal (y compris celle-ci).
Permettez-moi de vous raconter une histoire sur la naissance d'un objet au milieu du chaos d'un code procédural en bas à droite, rapide et sale, qui s'est produit, non pas par conception, mais par désespoir.
Mon stagiaire et moi programmons en binôme pour créer rapidement du code à jeter pour gratter une page Web. Nous n'avons absolument aucune raison de nous attendre à ce que ce code vive longtemps, alors nous ne faisons que cogner quelque chose qui fonctionne. Nous saisissons la page entière sous forme de chaîne et découpons les éléments dont nous avons besoin de la manière la plus étonnamment cassante que vous puissiez imaginer. Ne jugez pas. Ça marche.
Maintenant, en faisant cela, j'ai créé des méthodes statiques pour effectuer le découpage. Mon stagiaire a créé un cours DTO qui ressemblait beaucoup au vôtre CatData
.
Lorsque j'ai regardé le DTO pour la première fois, cela m'a dérangé. Les années de dommages que Java a causés à mon cerveau m'ont fait reculer dans les domaines publics. Mais nous travaillions en C #. C # n'a pas besoin de getters et setters prématurés pour préserver votre droit à rendre les données immuables ou encapsulées plus tard. Sans changer l'interface, vous pouvez les ajouter quand vous le souhaitez. Peut-être juste pour que vous puissiez définir un point d'arrêt. Le tout sans rien dire à vos clients. Oui C #. Boo Java.
J'ai donc tenu ma langue. J'ai regardé pendant qu'il utilisait mes méthodes statiques pour initialiser cette chose avant de l'utiliser. Nous en avions environ 14. C'était laid, mais nous n'avions aucune raison de nous en soucier.
Ensuite, nous en avions besoin ailleurs. Nous nous sommes retrouvés à vouloir copier et coller le code. 14 lignes d'initialisation sont lancées. Ça commençait à devenir douloureux. Il a hésité et m'a demandé des idées.
À contrecœur, je lui ai demandé: «considéreriez-vous un objet?
Il regarda son DTO et plissa le visage de confusion. "C'est un objet".
"Je veux dire un vrai objet"
"Hein?"
"Laissez-moi vous montrer quelque chose. Vous décidez si c'est utile"
J'ai choisi un nouveau nom et j'ai rapidement préparé quelque chose qui ressemblait un peu à ceci:
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);
}
}
Cela n'a rien fait que les méthodes statiques ne faisaient pas déjà. Mais maintenant, j'avais aspiré les 14 méthodes statiques dans une classe où elles pouvaient être seules avec les données sur lesquelles elles travaillaient.
Je n'ai pas forcé mon stagiaire à l'utiliser. Je l'ai juste offert et je l'ai laissé décider s'il voulait s'en tenir aux méthodes statiques. Je suis rentré chez moi en pensant qu'il s'en tiendrait probablement à ce qu'il avait déjà travaillé. Le lendemain, j'ai découvert qu'il l'utilisait dans un tas d'endroits. Il a désencombré le reste du code qui était toujours laid et procédural mais ce peu de complexité nous était maintenant caché derrière un objet. C'était un peu mieux.
Maintenant, chaque fois que vous y accédez, cela fait un bon travail. Un DTO est une belle valeur en cache rapide. J'étais inquiet à ce sujet, mais j'ai réalisé que je pouvais ajouter la mise en cache si nous en avions besoin sans toucher au code d'utilisation. Je ne vais donc pas déranger avant que nous nous en soucions.
Suis-je en train de dire que vous devriez toujours vous en tenir aux objets OO sur les DTO? Non. Le DTO brille lorsque vous devez traverser une frontière qui vous empêche de bouger. Les DTO ont leur place.
Mais les objets OO aussi. Apprenez à utiliser les deux outils. Découvrez ce que chacun coûte. Apprenez à laisser le problème, la situation et le stagiaire décider. Le dogme n'est pas votre ami ici.
Étant donné que ma réponse est déjà ridiculement longue, permettez-moi de vous dissuader de certaines idées fausses en examinant votre code.
Par exemple, une classe a généralement des membres de classe et des méthodes, par exemple:
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
}
}
Où est ton constructeur? Cela ne me montre pas assez pour savoir si c'est utile.
Mais après avoir lu sur le principe de responsabilité unique et le principe ouvert et fermé, je préfère séparer une classe en DTO et une classe auxiliaire avec des méthodes statiques uniquement, par exemple:
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
}
}
Je pense que cela correspond au principe de responsabilité unique, car maintenant la responsabilité de CatData est de conserver uniquement les données, ne se soucie pas des méthodes (également pour CatMethods).
Vous pouvez faire beaucoup de bêtises au nom du principe de responsabilité unique. Je pourrais dire que les cordes de chat et les cordes de chat devraient être séparées. Les méthodes de dessin et les images doivent toutes avoir leur propre classe. Que votre programme de course soit une responsabilité unique, vous ne devriez donc avoir qu'une seule classe. : P
Pour moi, la meilleure façon de suivre le principe de responsabilité unique est de trouver une bonne abstraction qui vous permet de mettre la complexité dans une boîte afin de pouvoir la cacher. Si vous pouvez lui donner un bon nom qui empêche les gens d'être surpris par ce qu'ils trouvent lorsqu'ils regardent à l'intérieur, vous l'avez assez bien suivi. S'attendre à ce qu'il dicte plus de décisions que cela pose problème. Honnêtement, vos deux listes de codes font cela, donc je ne vois pas pourquoi le SRP est important ici.
Et il correspond également au principe ouvert fermé, car l'ajout de nouvelles méthodes n'a pas besoin de modifier la classe CatData.
Et bien non. Le principe d'ouverture et de fermeture ne consiste pas à ajouter de nouvelles méthodes. Il s'agit de pouvoir changer l'implémentation des anciennes méthodes et de ne rien éditer. Rien qui vous utilise et pas vos anciennes méthodes. Au lieu de cela, vous écrivez du nouveau code ailleurs. Une certaine forme de polymorphisme le fera très bien. Ne voyez pas ça ici.
Ma question est, est-ce un bon ou un anti-modèle?
Eh bien, comment dois-je savoir? Regardez, le faire de toute façon a des avantages et des coûts. Lorsque vous séparez le code des données, vous pouvez modifier l'un ou l'autre sans avoir à recompiler l'autre. C'est peut-être extrêmement important pour vous. Peut-être que cela rend votre code inutilement compliqué.
Si cela vous fait vous sentir mieux, vous n'êtes pas si loin de ce que Martin Fowler appelle un objet paramètre . Vous n'êtes pas obligé de ne prendre que des primitives dans votre objet.
Ce que j'aimerais que vous fassiez, c'est de savoir comment faire votre séparation, ou non, dans l'un ou l'autre style de codage. Parce que croyez-le ou non, vous n'êtes pas obligé de choisir un style. Vous n'avez qu'à vivre avec votre choix.