Est-ce que “éviter le problème du yo-yo” est une raison valable pour permettre à “l'obsession primitive”?

42

Selon Quand l'obsession primitive n'est-elle pas une odeur de code? , Je devrais créer un objet ZipCode pour représenter un code postal au lieu d’un objet String.

Cependant, dans mon expérience, je préfère voir

public class Address{
    public String zipCode;
}

au lieu de

public class Address{
    public ZipCode zipCode;
}

parce que je pense que la dernière demande que je passe à la classe ZipCode pour comprendre le programme.

Et je crois que j'ai besoin de passer d'une classe à l'autre pour voir la définition si tous les champs de données primitifs étaient remplacés par une classe, ce qui donne l'impression de souffrir du problème du yo-yo (un anti-motif).

Je voudrais donc déplacer les méthodes ZipCode dans une nouvelle classe, par exemple:

Vieux:

public class ZipCode{
    public boolean validate(String zipCode){
    }
}

Nouveau:

public class ZipCodeHelper{
    public static boolean validate(String zipCode){
    }
}

de sorte que seul celui qui doit valider le code postal dépend de la classe ZipCodeHelper. Et j’ai trouvé un autre «avantage» à garder l’obsession primitive: elle garde la classe comme si elle se présentait sous une forme sérialisée, par exemple: une table d’adresses avec une colonne de chaîne zipCode.

Ma question est la suivante: "éviter le problème du yo-yo" (passer d'une définition de classe à une autre) est-il une raison valable d'autoriser "l'obsession primitive"?

ocomfd
la source
9
@ jpmc26 Alors vous seriez surpris de voir à quel point notre objet code postal est complexe - ne dis pas que c'est vrai, mais il existe
Jared Goguen
9
@ jpmc26, je ne vois pas comment vous passez de "complexe" à "mal conçu". Un code complexe est souvent le résultat d'un simple code entrant en contact avec la complexité du monde réel plutôt que du monde idéal que nous pourrions souhaiter exister. "Revenons à cette fonction de deux pages. Oui, je sais, c’est une fonction simple qui permet d’afficher une fenêtre, mais elle a laissé pousser de petits poils et des trucs dessus et personne ne sait pourquoi. Eh bien, je vais vous dire pourquoi: ce sont des bogues des correctifs. "
Kyralessa
19
@ jpmc26 - le point d'encapsuler des objets tels que ZipCode est du type sécurité. Le code postal n'est pas une chaîne, c'est un code postal. Si une fonction attend un code postal, vous ne devriez pouvoir lui transmettre qu'un code postal, pas une chaîne.
Davor Ždralo le
4
Cela donne une impression très spécifique à une langue, différentes langues font des choses différentes ici. @ DavorŽdralo Dans le même sens, nous devrions également ajouter beaucoup de types numériques. "seuls les entiers positifs", "seuls les nombres pairs" peuvent également être des types.
paul23
6
@ paul23 Oui, en effet, et la principale raison pour laquelle nous n'en avons pas, c'est que beaucoup de langages ne supportent pas les manières élégantes de les définir. Il est parfaitement raisonnable de définir "age" comme un type différent de "température en degrés Celsius", ne serait-ce que pour que "userAge == currentTemperature" soit détecté comme un non-sens.
IMSoP

Réponses:

116

L'hypothèse est qu'il n'est pas nécessaire de passer à la classe ZipCode pour comprendre la classe Address. Si ZipCode est bien conçu, le résultat doit être évident en lisant simplement la classe Address.

Les programmes ne sont pas lus de bout en bout - généralement, les programmes sont beaucoup trop complexes pour rendre cela possible. Vous ne pouvez pas garder tout le code d'un programme dans votre esprit en même temps. Nous utilisons donc des abstractions et des encapsulations pour "diviser" le programme en unités significatives, de sorte que vous puissiez consulter une partie du programme (par exemple, la classe Address) sans avoir à lire tout le code dont il dépend.

Par exemple, je suis sûr que vous ne lisez pas le code source de String chaque fois que vous rencontrez String dans le code.

Renommer la classe de ZipCode en ZipCodeHelper suggère désormais deux concepts distincts: un code postal et un assistant de code postal. Donc, deux fois plus complexe. Et maintenant, le système de types ne peut pas vous aider à faire la distinction entre une chaîne arbitraire et un code postal valide car ils ont le même type. C'est là que "l'obsession" est appropriée: vous suggérez une alternative plus complexe et moins sûre simplement parce que vous voulez éviter un simple type d'enveloppe autour d'une primitive.

L'utilisation d'une primitive est justifiée à mon humble avis dans les cas où il n'y a aucune validation ou autre logique dépendant de ce type particulier. Mais dès que vous ajoutez une logique, il est beaucoup plus simple que cette logique soit encapsulée avec le type.

En ce qui concerne la sérialisation, je pense que cela ressemble à une limitation du cadre que vous utilisez. Vous devriez sûrement pouvoir sérialiser un ZipCode sur une chaîne ou le mapper sur une colonne dans une base de données.

JacquesB
la source
2
Je suis d’accord avec les "unités significatives" (principale), mais pas tellement que code postal et validation de code postal sont le même concept. ZipCodeHelper(que je préférerais appeler ZipCodeValidator) pourrait très bien établir une connexion à un service Web pour faire son travail. Cela ne ferait pas partie de la responsabilité unique "détenir les données du code postal". Rendre le système de types non autorisé par des codes postaux invalides peut toujours être obtenu en rendant le ZipCodeconstructeur l'équivalent du paquet-privé de Java et en l'appelant avec un ZipCodeFactoryqui appelle toujours le validateur.
R. Schmitz le
16
@ R.Schmitz: Ce n'est pas ce que "responsabilité" signifie au sens du principe de responsabilité unique. Mais dans tous les cas, vous devez bien sûr utiliser autant de classes que vous le souhaitez tant que vous encapsulez le code postal et sa validation. L'OP suggère d'utiliser un assistant au lieu d' encapsuler le code postal, ce qui est une mauvaise idée.
JacquesB
1
Je veux respectueusement en désaccord. SRP signifie qu'une classe doit avoir "une et une seule raison de changer" (changement dans "en quoi consiste un code postal" ou "comment il est validé"). Ce cas spécifique est développé dans l'ouvrage Clean Code : " Les objets cachent leurs données derrière des abstractions et exposent des fonctions qui agissent sur ces données. La structure de données expose leurs données et n'a pas de fonctions significatives. " - ZipCodeserait une "structure de données". et ZipCodeHelperun "objet". Dans tous les cas, je pense que nous convenons qu'il ne devrait pas être nécessaire de passer des connexions Web au constructeur ZipCode.
R. Schmitz
9
L'utilisation d'une primitive est justifiée à mon humble avis dans les cas où il n'y a aucune validation ou autre logique dépendant de ce type particulier. => Je ne suis pas d'accord. Même si toutes les valeurs sont valides, je préférerais toujours transmettre la sémantique au langage plutôt que d'utiliser des primitives. Si une fonction peut être appelée sur un type primitif dépourvu de sens pour son usage sémantique actuel, alors ce ne devrait pas être un type primitif, il devrait s'agir d'un type approprié avec uniquement les fonctions sensibles définies. (Par exemple, utiliser intcomme identifiant permet de multiplier un identifiant par un identifiant ...)
Matthieu M.
@ R.Schmitz Je pense que les codes postaux sont un mauvais exemple de la distinction que vous faites. Quelque chose qui change souvent pourrait être un candidat pour séparer Fooet FooValidatorclasses. Nous pourrions avoir une ZipCodeclasse qui valide le format et une classe qui rencontre un ZipCodeValidatorservice Web pour vérifier qu'une mise en forme correcte ZipCodeest bien actuelle. Nous savons que les codes postaux changent. Mais dans la pratique, nous allons avoir une liste de codes ZIP valides encapsulés dans ZipCodeou dans une base de données locale.
No U
55

Si peut faire:

new ZipCode("totally invalid zip code");

Et le constructeur de ZipCode fait:

ZipCodeHelper.validate("totally invalid zip code");

Ensuite, vous avez cassé l’encapsulation et ajouté une dépendance plutôt stupide à la classe ZipCode. Si le constructeur n'appelle pas,ZipCodeHelper.validate(...) vous avez une logique isolée dans son propre îlot sans l'appliquer réellement. Vous pouvez créer des codes postaux invalides.

La validateméthode doit être une méthode statique sur la classe ZipCode. Maintenant, la connaissance d'un code postal "valide" est intégrée à la classe ZipCode. Étant donné que vos exemples de code ressemblent à Java, le constructeur de ZipCode devrait émettre une exception si un format incorrect est donné:

public class ZipCode {
    private String zipCode;

    public ZipCode(string zipCode) {
        if (!validate(zipCode))
            throw new IllegalFormatException("Invalid zip code");

        this.zipCode = zipCode;
    }

    public static bool validate(String zipCode) {
        // logic to check format
    }

    @Override
    public String toString() {
        return zipCode;
    }
}

Le constructeur vérifie le format et lève une exception, empêchant ainsi la création de codes postaux non valides. La validateméthode statique étant disponible pour d'autres codes, la logique de vérification du format est encapsulée dans la classe ZipCode.

Il n'y a pas de "yo-yo" dans cette variante de la classe ZipCode. C'est ce qu'on appelle la programmation orientée objet.


Nous allons également ignorer l'internationalisation ici, ce qui peut nécessiter une autre classe appelée ZipCodeFormat ou PostalService (par exemple, PostalService.isValidPostalCode (...), PostalService.parsePostalCode (...), etc.).

Greg Burghardt
la source
28
Remarque: le principal avantage de l'approche de @Greg Burkhardt est que si quelqu'un vous donne un objet ZipCode, vous pouvez être sûr qu'il contient une chaîne valide sans avoir à la vérifier à nouveau, car son type et le fait qu'il ait été construit avec succès vous donnent cette garantie. Si, au lieu de cela, vous passez des chaînes, vous pourriez ressentir le besoin "d’affirmer valider (zipCode)" à divers endroits de votre code juste pour vous assurer que vous avez un code postal valide, mais avec un objet ZipCode construit avec succès, vous pouvez être sûr que son contenu est valide sans avoir à les vérifier à nouveau.
Certains gars
3
@ R.Schmitz: La ZipCode.validateméthode est la pré-vérification qui peut être effectuée avant d'appeler un constructeur qui lève une exception.
Greg Burghardt le
10
@ R.Schmitz: Si une exception vexante vous préoccupe, une autre approche de la construction consiste à rendre le constructeur ZipCode privé et à fournir une fonction de fabrique statique publique (Zipcode.create?) Qui effectue la validation des paramètres transmis, renvoie null si échec, et sinon construit un objet ZipCode et le renvoie. L'appelant devra toujours vérifier une valeur de retour null, bien sûr. Par contre, si vous avez l’habitude, par exemple, de toujours valider (regex? Validate? Etc.) avant de construire un ZipCode, l’exception n’est peut-être pas si frustrante dans la pratique.
Un mec le
11
Une fonction d'usine qui renvoie un <ZipCode> facultatif est également une possibilité. Ensuite, l'appelant n'a pas d'autre choix que de gérer explicitement une éventuelle défaillance de la fonction d'usine. Quoi qu'il en soit, dans les deux cas, l'erreur sera découverte quelque part près de l'endroit où elle a été créée plutôt que peut-être beaucoup plus tard, par le code client éloigné du problème d'origine.
Certains gars
6
Vous ne pouvez pas valider ZipCode de manière indépendante, alors ne le faites pas. Vous avez vraiment besoin de l’objet Pays pour rechercher les règles de validation ZipCode / PostCode.
Joshua
11

Si vous vous posez beaucoup de questions sur cette question, le langage que vous utilisez n'est peut-être pas le bon outil pour le poste. Ce type de "primitives de type domaine" est trivialement facile à exprimer, par exemple, en F #.

Vous pouvez y écrire, par exemple:

type ZipCode = ZipCode of string
type Town = Town of string

type Adress = {
  zipCode: ZipCode
  town: Town
  //etc
}

let adress1 = {
  zipCode = ZipCode "90210"
  town = Town "Beverly Hills"
}

let faultyAdress = {
  zipCode = "12345"  // <-Compiler error
  town = adress1.zipCode // <- Compiler error
}

Ceci est vraiment utile pour éviter les erreurs courantes, comme comparer les identifiants d'entités différentes. Et comme ces primitives typées sont beaucoup plus légères qu'une classe C # ou Java, vous finirez par les utiliser.

Guran
la source
Intéressant - à quoi ressemblerait-il si vous vouliez appliquer la validation ZipCode?
Hulk
4
@ Hulk Vous pouvez écrire le style OO en F # et transformer les types en classes. Cependant, je préfère le style fonctionnel, déclarant le type avec le type ZipCode = ZipCode privé de la chaîne et en ajoutant un module ZipCode avec une fonction de création. Il y a quelques exemples ici: gist.github.com/swlaschin/54cfff886669ccab895a
Guran Le
@ Bent-Tranberg Merci pour l'édition. Vous avez raison, une simple abréviation ne donne pas la sécurité du type de compilation.
Guran
Si vous avez reçu par la poste mon premier commentaire, que j'ai supprimé, c'est parce que j'ai d'abord mal compris votre source. Je ne l'ai pas lu assez attentivement. Lorsque j'ai essayé de le compiler, j'ai finalement réalisé que vous essayiez réellement de le démontrer, alors j'ai décidé de le modifier pour le corriger.
Bent Tranberg le
Ouais. Ma source originale était valide, y compris malheureusement l'exemple qui était SUPPOSÉ comme invalide. Doh! Devrait avoir juste lié à Wlaschin au lieu de taper le code moi-même :) fsharpforfunandprofit.com/posts/…
Guran
6

La réponse dépend entièrement de ce que vous voulez réellement faire avec les codes postaux. Voici deux possibilités extrêmes:

(1) Il est garanti que toutes les adresses se trouvent dans un seul pays. Aucune exception du tout. (Par exemple, aucun client étranger ou aucun employé dont l'adresse personnelle est à l'étranger alors qu'ils travaillent pour un client étranger.) Ce pays possède des codes postaux et on peut s'attendre à ce qu'ils ne soient jamais sérieusement problématiques (c'est-à-dire qu'ils ne nécessitent pas de saisie libre comme "actuellement D4B 6N2, mais cela change toutes les 2 semaines"). Les codes postaux ne sont pas utilisés uniquement pour l'adressage, mais également pour la validation des informations de paiement ou à des fins similaires. - Dans ces circonstances, une classe de code postal a beaucoup de sens.

(2) Les adresses pouvant se trouver dans presque tous les pays, des dizaines ou des centaines de schémas d'adressage avec ou sans code postal (et avec des milliers d'exceptions étranges et de cas particuliers) sont pertinents. Un code "ZIP" est uniquement demandé pour rappeler aux gens des pays où les codes ZIP sont utilisés de ne pas oublier de fournir les leurs. Les adresses ne sont utilisées que de sorte que si une personne perd l'accès à son compte et peut prouver son nom et son adresse, l'accès sera restauré. - Dans ces conditions, les classes de code postal pour tous les pays concernés constitueraient un effort énorme. Heureusement, ils ne sont pas du tout nécessaires.


la source
3

Les autres réponses ont parlé de la modélisation de domaine OO et de l'utilisation d'un type plus riche pour représenter votre valeur.

Je ne suis pas en désaccord, surtout à cause de l'exemple de code que vous avez posté.

Mais je me demande également si cela répond effectivement au titre de votre question.

Considérez le scénario suivant (tiré d'un projet réel sur lequel je travaille):

Vous avez une application distante sur un appareil de terrain qui communique avec votre serveur central. L'un des champs de la base de données pour l'entrée de périphérique est un code postal pour l'adresse à laquelle se trouve l'appareil de terrain. Vous ne vous souciez pas du code postal (ni du reste de l'adresse, d'ailleurs). Toutes les personnes qui s’intéressent à cette question se trouvent de l’autre côté de la frontière HTTP: vous n’êtes que la seule source de vérité des données. Il n'a pas sa place dans votre modélisation de domaine. Vous devez simplement l'enregistrer, le valider, le stocker et, sur demande, le mélanger dans un blob JSON à des points situés ailleurs.

Dans ce scénario, faire beaucoup plus que valider l'insertion avec une contrainte regex SQL (ou son équivalent ORM) constitue probablement une perte excessive de la variété YAGNI.

Jared Smith
la source
6
Votre contrainte d'expression rationnelle SQL peut être considérée comme un type qualifié - dans votre base de données, le code postal n'est pas stocké sous le nom "VarChar", mais "VarChar contraint par cette règle". Dans certains SGBD, vous pouvez facilement nommer cette contrainte type + comme un "type de domaine" réutilisable, et nous revenons à l'endroit recommandé pour donner aux données un type explicite. Je suis d'accord avec votre réponse en principe, mais ne croyez pas que l'exemple corresponde; un meilleur exemple serait si vos données sont des "données de capteur brutes", et le type le plus significatif est "tableau d'octets" car vous ne savez pas ce que les données signifient.
IMSoP
@IMSoP point intéressant. Je ne suis pas sûr que je sois d’accord: vous pouvez valider un code postal de chaîne en Java (ou n’importe quel autre langage) avec une expression rationnelle, tout en conservant un code plus riche. En fonction de la logique du domaine, une manipulation supplémentaire peut être nécessaire (par exemple, s'assurer que le code postal correspond à l'état, ce qui serait difficile / impossible à valider avec regex).
Jared Smith le
Vous pouvez le faire , mais dès que vous le faites, vous lui attribuez un comportement spécifique à un domaine. C’est exactement ce que les articles cités disent devrait conduire à la création d’un type personnalisé. La question n'est pas de savoir si vous pouvez le faire dans l'un ou l'autre style, mais si vous devriez le faire , en supposant que votre langage de programmation vous donne le choix.
IMSoP le
Vous pouvez modéliser une telle chose en tant que RegexValidatedString, contenant la chaîne elle-même et l'expression régulière utilisée pour la valider. Mais à moins que chaque instance ait une expression rationnelle unique (ce qui est possible mais peu probable), cela semble un peu idiot et gaspille en mémoire (et éventuellement en temps de compilation regex). Donc, soit vous placez l'expression rationnelle dans une table distincte et vous laissez une clé de recherche dans chaque instance pour la trouver (ce qui est sans doute pire en raison de l'indirection), soit vous trouvez un moyen de la stocker une fois pour chaque type courant de valeur partageant cette règle - - par exemple. un champ statique sur un type de domaine, ou une méthode équivalente, comme dit IMSoP.
Miral
2

L' ZipCodeabstraction ne peut avoir un sens que si votre Addressclasse n'a pas également une TownNamepropriété. Sinon, vous avez une demi-abstraction: le code postal désigne la ville, mais ces deux informations connexes se trouvent dans des classes différentes. Cela n'a pas vraiment de sens.

Cependant, même dans ce cas, ce n'est toujours pas une application correcte (ou plutôt une solution à) l'obsession primitive; qui, si je comprends bien, se concentre principalement sur deux choses:

  1. Utilisation de primitives comme valeurs d'entrée (ou même de sortie) d'une méthode, en particulier lorsqu'une collection de primitives est nécessaire.
  2. Les classes qui développent des propriétés supplémentaires au fil du temps sans jamais se demander si certaines d’entre elles doivent être regroupées dans une sous-classe distincte.

Votre cas est ni. Une adresse est un concept bien défini avec des propriétés clairement nécessaires (rue, numéro, code postal, ville, état, pays, ...). Il y a peu à aucune raison de briser ces données car il a une seule responsabilité: désigner un endroit sur Terre. Une adresse nécessite tous ces champs pour être significative. Une demi-adresse est inutile.

C’est ainsi que vous savez que vous n’avez pas besoin de subdiviser davantage: une nouvelle division compromettrait l’intention fonctionnelle de la Addressclasse. De même, vous n'avez pas besoin d'utiliser une Namesous - classe dans la Personclasse, à moins que Name(sans personne attachée) ne soit un concept significatif dans votre domaine. Ce qui n'est (généralement) pas. Les noms sont utilisés pour identifier les personnes, ils n'ont généralement aucune valeur par eux-mêmes.

Flater
la source
1
@RikD: D'après la réponse: "vous n'avez pas besoin d'une Namesous - classe pour être utilisée dans la Personclasse, à moins que Nom (sans personne attachée) ne soit un concept significatif dans votre domaine ." Lorsque vous avez une validation personnalisée pour les noms, le nom est devenu un concept significatif dans votre domaine. que j'ai explicitement mentionné comme cas d'utilisation valide pour utiliser un sous-type. Deuxièmement, pour la validation du code postal, vous introduisez des hypothèses supplémentaires, telles que les codes postaux devant suivre le format d'un pays donné. Vous abordez un sujet qui dépasse de loin l'objectif de la question d'OP.
Flater
5
" Une adresse est un concept bien défini avec des propriétés clairement nécessaires (rue, numéro, code postal, ville, état, pays). " - Eh bien, c'est tout simplement faux. Pour une bonne solution, consultez le formulaire d'adresse d'Amazon.
R. Schmitz le
4
@Flater Eh bien, je ne vous blâmerai pas de ne pas avoir lu la liste complète des mensonges, car elle est assez longue, mais elle contient littéralement "Les adresses auront une rue", "Une adresse nécessite à la fois une ville et un pays", "Une adresse aura un code postal "etc., ce qui est contraire à ce que dit la phrase citée.
R. Schmitz le
8
@GregBurghardt "Le code postal suppose le service postal des États-Unis et vous pouvez dériver le nom de la ville à partir du code postal. Les villes peuvent avoir plusieurs codes postaux, mais chaque code postal est associé à une seule ville." Ce n'est pas correct en général. J'ai un code postal utilisé principalement pour une ville voisine, mais ma résidence n'y est pas située. Les codes postaux ne sont pas toujours alignés sur les limites gouvernementales. Par exemple, 42223 contient des comtés de TN et de KY .
JimmyJames
2
En Autriche, il existe une vallée uniquement accessible depuis l’Allemagne ( en.wikipedia.org/wiki/Kleinwalsertal ). Il existe un traité spécial pour cette région qui inclut, entre autres, que les adresses de cette région ont des codes postaux autrichiens et allemands. Donc, en général, vous ne pouvez même pas supposer qu'une adresse n'a qu'un seul code postal valide;)
Hulk
1

De l'article:

Plus généralement, le problème du yo-yo peut également concerner toute situation dans laquelle une personne doit basculer sans cesse entre différentes sources d'informations afin de comprendre un concept.

Le code source est lu beaucoup plus souvent qu'il n'est écrit. Par conséquent, le problème du yo-yo, qui consiste à basculer entre plusieurs fichiers, est une préoccupation.

Cependant, non , le problème du yo-yo semble beaucoup plus pertinent lorsqu'il s'agit de modules ou de classes fortement interdépendants (qui s'appellent entre eux). Ce sont des cauchemars spéciaux à lire, et c’est probablement ce que l’intéressant du problème du yo-yo avait en tête.

Cependant - oui , il est important d'éviter de trop nombreuses couches d'abstraction!

Toutes les abstractions non triviales, dans une certaine mesure, ont des fuites. - la loi des abstractions qui fuient.

Par exemple, je ne souscris pas à l'hypothèse formulée dans la réponse de mmmaaa selon laquelle "il n'est pas nécessaire de lire la classe ZipCode pour comprendre la classe d'adresse". Mon expérience a été que vous faites - au moins les premières fois où vous lisez le code. Cependant, comme d' autres l' ont noté, il y a des moments où une ZipCodeclasse est appropriée.

YAGNI (il n'y en aura pas besoin) est un meilleur modèle à suivre pour éviter le code de lasagne (code avec trop de couches) - les abstractions, telles que les types et les classes sont là pour aider le programmeur, et ne doivent pas être utilisées à moins qu'elles ne le soient une aide.

Je vise personnellement à "enregistrer des lignes de code" (et bien sûr à "enregistrer des fichiers / modules / classes", etc.). Je suis persuadé que certains voudront bien appliquer l'épithète d '"obsédé primitif" - je trouve qu'il est plus important d'avoir un code sur lequel il est facile de raisonner que de s'inquiéter des étiquettes, des motifs et des anti-motifs. Le choix correct du moment où créer une fonction, un module / fichier / classe ou placer une fonction à un emplacement commun est très situationnel. Je vise environ 3-100 fonctions de ligne, 80-500 fichiers de ligne et "1, 2, n" pour un code de bibliothèque réutilisable ( SLOC - sans commentaires ni passe-partout; je souhaite généralement au moins un minimum de SLOC supplémentaire par ligne obligatoire passe-partout).

La plupart des tendances positives sont apparues lorsque les développeurs ont fait exactement cela, quand ils en avaient besoin . Il est beaucoup plus important d'apprendre à écrire du code lisible que d'essayer d'appliquer des modèles sans résoudre le même problème. Tout bon développeur peut implémenter le modèle d'usine sans l'avoir vu auparavant, dans les cas peu communs où il convient parfaitement à son problème. J'ai utilisé le modèle d'usine, le modèle d'observateur, et probablement des centaines d'autres, sans connaître leur nom (c'est-à-dire, existe-t-il un "modèle d'affectation variable"?). Pour une expérience amusante (voyez combien de modèles GoF sont intégrés au langage JS ) , j'ai arrêté de compter après environ 12-15 ans en 2009. Le modèle Factory est aussi simple que de renvoyer un objet depuis un constructeur JS, par exemple - nul besoin de un WidgetFactory.

Donc, oui , parfois, ZipCode c'est une bonne classe. Cependant, non , le problème du yo-yo n'est pas strictement pertinent.

Iiridayn
la source
0

Le problème du yo-yo n’est pertinent que si vous devez retourner en arrière. Cela est causé par une ou deux choses (parfois les deux):

  1. Mauvaise appellation. ZipCode semble bien, ZoneImprovementPlanCode va exiger un regard de la plupart des gens (et les rares qui ne seront pas impressionnés).
  2. Couplage inapproprié. Dites-vous que la classe ZipCode a une recherche d'indicatif régional. Vous pensez peut-être que cela a du sens parce que c'est pratique, mais ce n'est pas vraiment lié au ZipCode, et cela veut dire que les gens ne savent plus où aller.

Si vous pouvez regarder le nom et avoir une idée raisonnable de ce qu'il fait, et que les méthodes et les propriétés font des choses raisonnablement évidentes, vous n'avez pas besoin d'aller voir le code, vous pouvez simplement l'utiliser. C'est tout le sens des classes en premier lieu: ce sont des morceaux de code modulaires qui peuvent être utilisés et développés séparément. Si vous devez regarder autre chose que l'API de la classe pour voir ce qu'elle fait, c'est au mieux un échec partiel.

jmoreno
la source
-1

Rappelez-vous, il n'y a pas de solution miracle. Si vous écrivez une application extrêmement simple qui doit être analysée rapidement, une simple chaîne peut suffire. Toutefois, dans 98% des cas, un objet de valeur, tel que décrit par Eric Evans dans DDD, conviendrait parfaitement. Vous pouvez facilement voir tous les avantages procurés par les objets de valeur en les parcourant.

Alexios Plomaritis
la source