La séparation de la plupart des classes en classes de champ de données uniquement et en classes de méthode uniquement (si possible) est-elle un bien ou un anti-modèle?

10

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
    }
}

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). Et il correspond également au principe ouvert fermé, car l'ajout de nouvelles méthodes n'a pas besoin de modifier la classe CatData.

Ma question est, est-ce un bon ou un anti-modèle?

ocomfd
la source
15
Faire n'importe quoi aveuglément partout parce que vous avez appris un nouveau concept est toujours un anti-modèle.
Kayaman
4
À quoi serviraient les classes s'il est toujours préférable d'avoir des données distinctes des méthodes qui les modifient? Cela ressemble à une programmation non orientée objet (qui a certainement sa place). Puisqu'il est étiqueté "orienté objet", je suppose que vous souhaitez utiliser les outils fournis par OOP?
user1118321
4
Une autre question prouvant que le SRP est si mal compris qu'il devrait être interdit. Garder les données dans un tas de domaines publics n'est pas une responsabilité .
user949300
1
@ user949300, si la classe est responsable de ces champs, les déplacer ailleurs sur l'application n'aura bien sûr aucun effet. Ou pour le dire autrement, vous vous trompez: ils sont à 100% une responsabilité.
David Arno
2
@DavidArno et R Schmitz: les champs contenant ne comptent pas comme une responsabilité aux fins de SRP. Si tel était le cas, il ne pourrait pas y avoir de POO, car tout serait un DTO, puis des classes séparées contiendraient des procédures pour travailler sur les données. Une seule responsabilité appartient à une seule partie prenante . (Bien que cela semble changer légèrement à chaque itération du principal)
user949300

Réponses:

10

Vous avez montré deux extrêmes ("tout ce qui est privé et toutes les méthodes (peut-être sans rapport) dans un seul objet" vs "tout ce qui est public et aucune méthode à l'intérieur de l'objet"). À mon humble avis, la bonne modélisation OO n'en fait pas partie , le point idéal est quelque part au milieu.

Un test décisif des méthodes ou de la logique qui appartient à une classe et de ce qui appartient à l'extérieur consiste à examiner les dépendances que les méthodes introduiront. Les méthodes qui n'introduisent pas de dépendances supplémentaires sont très bien, tant qu'elles s'adaptent bien à l'abstraction de l'objet donné . Les méthodes qui nécessitent des dépendances externes supplémentaires (comme une bibliothèque de dessins ou une bibliothèque d'E / S) conviennent rarement. Même si vous faites disparaître les dépendances en utilisant l'injection de dépendances, je réfléchirais encore à deux fois si le placement de telles méthodes dans la classe de domaine est vraiment nécessaire.

Par conséquent, ni vous ne devez rendre chaque membre public, ni avoir à implémenter des méthodes pour chaque opération sur un objet à l'intérieur de la classe. Voici une suggestion alternative:

public class Cat{
    private String name;
    private int weight;
    private Image image;

    public String getInfo(){
        return "Name:"+this.name+",weight:"+this.weight;
    }
    public Image getImage(){
        return image;
    }
}

Désormais, l' Catobjet fournit suffisamment de logique pour permettre au code environnant de s'implémenter facilement printInfoet drawsans exposer tous les attributs en public. Le bon endroit pour ces deux méthodes n'est probablement pas une classe divine CatMethods(car printInfoet ce drawsont probablement des préoccupations différentes, donc je pense qu'il est très peu probable qu'elles appartiennent à la même classe).

Je peux imaginer un CatDrawingControllerqui implémente draw(et utilise peut-être l'injection de dépendances pour obtenir un objet Canvas). Je peux également imaginer une autre classe qui implémente une sortie de console et utilise getInfo( printInfopeut donc devenir obsolète dans ce contexte). Mais pour prendre des décisions judicieuses à ce sujet, il faut connaître le contexte et la façon dont la Catclasse sera réellement utilisée.

C'est en fait la façon dont j'ai interprété les critiques du modèle de domaine anémique de Fowler - pour une logique généralement réutilisable (sans dépendances externes), les classes de domaine elles-mêmes sont un bon endroit, donc elles devraient être utilisées pour cela. Mais cela ne veut pas dire y mettre en œuvre une logique, bien au contraire.

Notez également que l'exemple ci-dessus laisse encore de la place pour prendre une décision concernant la (im) mutabilité. Si la Catclasse n'expose pas de setters, et Imageest immuable elle-même, cette conception permettra de rendre Catimmuable (ce que l'approche DTO ne fera pas). Mais si vous pensez que l'immuabilité n'est pas nécessaire ou inutile pour votre cas, vous pouvez également aller dans cette direction.

Doc Brown
la source
Déclencher des downvoters heureux, cela devient ennuyeux. Si vous avez des critiques, faites-le moi savoir. Je serai heureux de clarifier tout malentendu, si vous en avez.
Doc Brown
Bien que je sois généralement d'accord avec votre réponse ... je pense getInfo()que cela pourrait induire en erreur quelqu'un qui pose cette question. Il mélange subtilement les responsabilités de présentation printInfo(), faisant ainsi échouer le but de la DrawingControllerclasse
Adriano Repetti
@AdrianoRepetti Je ne vois pas que cela va à l'encontre du but de DrawingController. Je suis d'accord getInfo()et ce printInfo()sont des noms horribles. Considérez toString()etprintTo(Stream stream)
candied_orange
@candid ce n'est pas (juste) le nom mais ce qu'il fait. Ce code ne concerne qu'un détail de présentation et nous avons simplement résumé la sortie et non le contenu traité et sa logique.
Adriano Repetti
@AdrianoRepetti: honnêtement, ce n'est qu'un exemple artificiel, pour s'adapter à la question d'origine et démontrer mon point. Dans un système réel, il y aura un contexte environnant qui permettra de prendre des décisions sur de meilleures méthodes et noms, bien sûr.
Doc Brown
6

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.

candied_orange
la source
2

Vous êtes tombé sur un sujet de débat qui a suscité un débat parmi les développeurs depuis plus d'une décennie. En 2003, Martin Fowler a inventé l'expression «modèle de domaine anémique» (ADM) pour décrire cette séparation des données et des fonctionnalités. Lui - et d'autres qui sont d'accord avec lui - soutiennent que les «modèles de domaine riches» (mélange de données et de fonctionnalités) sont des «OO appropriés» et que l'approche ADM est un anti-modèle non OO.

Il y a toujours eu ceux qui rejettent cet argument et ce côté de l'argument est devenu plus fort et plus audacieux ces dernières années avec l'adoption par plus de développeurs de techniques de développement fonctionnel. Cette approche encourage activement la séparation des données et des préoccupations fonctionnelles. Les données doivent être immuables autant que possible, de sorte que l'encapsulation de l'état mutable devient une préoccupation non. Il n'y a aucun avantage à attacher des fonctions directement à ces données dans de telles situations. Que ce soit alors "pas OO" ou pas n'a absolument aucun intérêt pour ces gens.

Quel que soit le côté de la clôture sur lequel vous vous asseyez (je suis très fermement du côté "Martin Fowler parle d'une charge de vieux tosh" BTW), votre utilisation de méthodes statiques pour printInfoet drawest presque universellement mal vue. Il est difficile de se moquer des méthodes statiques lors de l'écriture de tests unitaires. Donc, s'ils ont des effets secondaires (tels que l'impression ou le dessin sur un écran ou un autre périphérique), ils ne doivent pas être statiques, ou doivent avoir l'emplacement de sortie transmis en tant que paramètre.

Vous pourriez donc avoir une interface:

public interface CatMethods {
    void printInfo(Cat cat);
    void draw(Cat cat);
}

Et une implémentation qui est injectée dans le reste de votre système lors de l'exécution (avec d'autres implémentations utilisées pour les tests):

internal class CatMethodsForScreen implements CatMethods {
    public void printInfo(Cat cat) {
        System.out.println("Name:"+cat.name+",weight:"+cat.weight);
    }

    public void draw(Cat cat) {
        //some draw code which uses cat.image
    }
}

Ou ajoutez des paramètres supplémentaires pour supprimer les effets secondaires de ces méthodes:

public static class CatMethods {
    public static void printInfo(Cat cat, OutputHandler output) {
        output.println("Name:"+cat.name+",weight:"+cat.weight);
    }

    public static void draw(Cat cat, Canvas canvas) {
        //some draw code which uses cat.image and draws it on canvas
    }
}
David Arno
la source
Mais pourquoi voudriez-vous essayer de chausse-pied un langage OO comme Java dans un langage fonctionnel, au lieu d'aller avec un langage fonctionnel dès le départ? Est-ce une sorte de "je déteste Java, mais tout le monde l'utilise donc je le dois, mais au moins je vais l'écrire à ma façon". Sauf si vous prétendez que le paradigme OO est obsolète et obsolète en quelque sorte. Je souscris à l'école de pensée "Si vous voulez X, vous savez où l'obtenir".
Kayaman
@Kayaman, " Je m'inscris à la" Si vous voulez X, vous savez où l'obtenir "école de pensée ". Je ne. Je trouve cette attitude à la fois myope et légèrement offensive. Je n'utilise pas Java, mais j'utilise C # et j'ignore certaines des fonctionnalités "réelles OO". Je n'aime pas l'héritage, les objets avec état et autres et j'écris beaucoup de méthodes statiques et de classes (finales) scellées. L'outillage et la taille de la communauté autour de C # sont sans égal. Pour F #, c'est beaucoup plus pauvre. Je souscris donc à l'école de pensée "Si vous voulez X, demandez à ce qu'elle soit ajoutée à votre outillage actuel".
David Arno
1
Je dirais qu'ignorer les aspects d'une langue est très différent d'essayer de mélanger et de faire correspondre des fonctionnalités d'autres langues. Je n'essaie pas non plus d'écrire "OO réel", car essayer de simplement suivre les règles (ou principes) dans une science inexacte comme la programmation ne fonctionne pas. Bien sûr, vous devez connaître les règles pour pouvoir les enfreindre. S'attaquer aveuglément aux fonctionnalités peut être mauvais pour le développement du langage. Aucune infraction, mais à mon humble avis, une partie de la scène de la PF semble sentir que "la PF est la plus grande chose depuis le pain en tranches, pourquoi les gens ne le comprennent-ils pas" Je ne dirais jamais (ou penser) que OO ou Java est "le meilleur".
Kayaman
1
@Kayaman, mais que signifie "essayer de mélanger et de faire correspondre des fonctionnalités d'autres langues"? Êtes-vous en train de dire que les fonctionnalités ne devraient pas être ajoutées à java, car "Java est un langage OO"? Dans l'affirmative, c'est un argument à courte vue à mon avis. Laissez le langage évoluer avec les désirs des développeurs. Je pense que les forcer à passer à une autre langue n'est pas la bonne approche. Bien sûr, certains "défenseurs de la PF" vont au-delà du fait que la PF est la meilleure chose qui soit. Et certains «défenseurs de l'OO» vont au-dessus du haut au sujet de l'OO ayant «gagné la bataille parce que c'est clairement supérieur». Ignorez-les tous les deux.
David Arno
1
Je ne suis pas anti-FP. Je l'utilise en Java où c'est utile. Mais si un programmeur dit "Je veux ça", c'est comme un client qui dit "Je veux ça". Les programmeurs ne sont pas des concepteurs de langage et les clients ne sont pas des programmeurs. J'ai voté contre votre réponse car vous dites que la «solution» d'OP est mal vue. Le commentaire dans la question est cependant plus succinct, c'est-à-dire qu'il a réinventé la programmation procédurale dans un langage OO. L'idée générale a cependant un sens, comme vous l'avez montré. Un modèle de domaine non anémique ne signifie pas que les données et le comportement sont exclusifs, et les rendus ou présentateurs externes seraient interdits.
Kayaman
0

DTO - Objets de transport de données

sont utiles pour cela. Si vous allez shunter des données entre des programmes ou des systèmes, les DTO sont souhaitables car ils fournissent un sous-ensemble gérable d'un objet qui ne concerne que la structure et le formatage des données. L'avantage étant que vous n'avez pas à synchroniser les mises à jour des méthodes complexes sur plusieurs systèmes (tant que les données sous-jacentes ne changent pas).

L'intérêt de OO est de lier étroitement les données et le code agissant sur ces données. Séparer un objet logique en classes séparées est généralement une mauvaise idée.

James Anderson
la source
-1

De nombreux modèles et principes de conception sont en conflit et, en fin de compte, seul votre jugement en tant que bon programmeur vous aidera à décider quels modèles doivent s'appliquer à un problème spécifique.

À mon avis, le principe Faire la chose la plus simple qui pourrait éventuellement fonctionner devrait gagner par défaut, sauf si vous avez une bonne raison de rendre la conception plus compliquée. Donc, dans votre exemple spécifique, j'opterais pour la première conception, qui est une approche orientée objet plus traditionnelle avec des données et des fonctions réunies dans la même classe.

Voici quelques questions que vous pourriez vous poser sur l'application:

  • Aurai-je un jour besoin de fournir une autre façon de rendre une image de chat? Si oui, l'héritage ne sera-t-il pas un moyen pratique de le faire?
  • Ma classe Cat doit-elle hériter d'une autre classe ou satisfaire une interface?

Répondre à l'un de ces critères peut vous conduire à une conception plus complexe avec des classes DTO et fonctionnelles distinctes.

Jordan Rieger
la source
-1

Est-ce un bon schéma?

Vous allez probablement obtenir des réponses différentes ici parce que les gens suivent des écoles de pensée différentes. Donc, avant même de commencer: cette réponse est basée sur la programmation orientée objet telle qu'elle est décrite par Robert C. Martin dans le livre "Clean Code".


"True" OOP

Pour autant que je sache, il y a 2 interprétations de la POO. Pour la plupart des gens, cela se résume à "un objet est une classe".

Cependant, j'ai trouvé l'autre école de pensée plus utile: "Un objet est quelque chose qui fait quelque chose". Si vous travaillez avec des classes, chaque classe serait l'une des deux choses: un " détenteur de données " ou un objet . Les détenteurs de données détiennent des données (duh), les objets font des choses. Cela ne signifie pas qu'un détenteur de données ne peut pas avoir de méthodes! Pensez à a list: A listne fait rien, mais il a des méthodes pour appliquer la façon dont il détient les données .

Détenteurs de données

Vous Catest un excellent exemple de détenteur de données. Bien sûr, en dehors de votre programme, un chat est quelque chose qui fait quelque chose , mais à l'intérieur de votre programme, a Catest une chaîne name, un int weightet une image image.

Que les détenteurs de données ne fassent rien ne signifie pas non plus qu'ils ne contiennent pas de logique métier! Si votre Catclasse a un constructeur nécessitant name, weightet image, vous avez réussi à encapsuler la règle métier que chaque chat en a. Si vous pouvez modifier weightaprès la Catcréation d' une classe, c'est une autre règle métier encapsulée. Ce sont des choses très basiques, mais cela signifie simplement qu'il est très important de ne pas se tromper - et de cette façon, vous vous assurez qu'il est impossible de se tromper.

Objets

Les objets font quelque chose. Si vous voulez des objets Clean *, nous pouvons changer cela en "Les objets ne font qu'une chose".

L'impression des informations sur le chat est le travail d'un objet. Par exemple, vous pouvez appeler cet objet " CatInfoLogger", avec une méthode publique Log(Cat). C'est tout ce que nous devons savoir de l'extérieur: cet objet enregistre les informations d'un chat et pour ce faire, il a besoin d'un Cat.

À l'intérieur, cet objet aurait des références à tout ce dont il a besoin pour remplir sa seule responsabilité de l'exploitation des chats. Dans ce cas, ce n'est qu'une référence à Systemfor System.out.println, mais dans la plupart des cas, ce seront des références privées à d'autres objets. Si la logique de formatage de la sortie avant de l'imprimer devient trop complexe, CatInfoLoggeril suffit d'obtenir une référence à un nouvel objet CatInfoFormatter. Si plus tard, chaque journal doit également être écrit dans un fichier, vous pouvez ajouter un CatToFileLoggerobjet qui le fait.

Détenteurs de données vs objets - résumé

Maintenant, pourquoi feriez-vous tout cela? Parce que maintenant changer une classe ne change qu'une chose ( la façon dont un chat est connecté dans l'exemple). Si vous changez un objet, vous changez seulement la façon dont une certaine action est effectuée; si vous changez de détenteur de données, vous ne modifiez que les données détenues et / ou de quelle manière elles sont détenues.

La modification des données peut également signifier que la façon dont quelque chose est fait doit changer, mais ce changement ne se produira que lorsque vous le réaliserez vous-même en modifiant l'objet responsable.

Trop fort?

Cette solution peut sembler un peu exagérée. Permettez-moi d'être franc: c'est le cas . Si tout votre programme est aussi grand que l'exemple, ne faites rien de ce qui précède - choisissez simplement l'une des façons proposées avec un tirage au sort. Il n'y a pas de risque de confusion autre code s'il est pas d' autre code.

Cependant, tout logiciel "sérieux" (= plus qu'un script ou 2) est probablement suffisamment gros pour bénéficier d'une telle structure.


Réponse

La séparation de la plupart des classes en classes DTO et [...] auxiliaires est-elle un bien ou un anti-modèle?

Transformer "la plupart des classes" (sans autre qualification) en DTO / détenteurs de données, n'est pas un anti-modèle, car ce n'est en fait pas un modèle - c'est plus ou moins aléatoire.

Cependant, la séparation des données et des objets est considérée comme un bon moyen de garder votre code propre *. Parfois, vous n'aurez besoin que d'un DTO plat et "anémique" et c'est tout. D'autres fois, vous avez besoin de méthodes pour appliquer la manière dont les données sont conservées. Ne mettez tout simplement pas la fonctionnalité de votre programme avec les données.


* C'est "Clean" avec un C majuscule comme le titre du livre, car - rappelez-vous le premier paragraphe - il s'agit d'une école de pensée, alors qu'il y en a aussi d'autres.

R. Schmitz
la source