Je suis tombé par hasard sur une entrée de blog décourageant l'utilisation de Strings en Java pour rendre votre code dépourvu de sémantique, suggérant d'utiliser plutôt des classes wrappers minces. Voici les exemples d’avant et d’après que cette entrée fournit pour illustrer le propos:
public void bookTicket(
String name,
String firstName,
String film,
int count,
String cinema);
public void bookTicket(
Name name,
FirstName firstName,
Film film,
Count count,
Cinema cinema);
En lisant des blogs sur la programmation, je suis arrivé à la conclusion que 90% est un non-sens, mais je me demande si c'est un argument valable. En quelque sorte, cela ne me convient pas, mais je ne peux pas déterminer exactement ce qui ne va pas avec le style de programmation.
Réponses:
L'encapsulation est là pour protéger votre programme contre les changements . La représentation d'un nom va-t-elle changer? Sinon, vous perdez votre temps et YAGNI s'applique.
Edit: J'ai lu le blog et il a une bonne idée. Le problème, c'est qu'il a beaucoup trop évolué. Quelque chose
String orderId
est vraiment mauvais, parce que vraisemblablement"!"£$%^&*())ADAFVF
n'est pas valideorderId
. Cela signifie queString
représente beaucoup plus de valeurs possibles qu'il n'y a deorderId
s valides . Cependant, pour quelque chose comme unname
, alors vous ne pouvez pas prédire ce qui pourrait ou non être un nom valide, et anyString
est validename
.En premier lieu, vous réduisez (correctement) les entrées possibles en données valides uniquement. Dans le second cas, vous n'avez pas réduit le nombre d'entrées valides possibles.
Modifier à nouveau: considérons le cas d'une entrée invalide. Si vous écrivez "Gareth Gobulcoque" comme votre nom, ça aura l'air ridicule, ce ne sera pas la fin du monde. Si vous entrez un OrderID invalide, il y a de grandes chances qu'il ne fonctionne tout simplement pas.
la source
Name
, vous ne pouvez pas transmettre accidentellement une autre valeur String non liée. Lorsque vous vous attendez à unName
, seul unName
testament est compilé et la chaîne "Je vous dois 5 $" ne peut être acceptée accidentellement. (Notez que ceci n’est pas lié à une validation de nom!)C'est juste fou :)
la source
var p = new Point(x: 10, y:20);
En outre, ce n’est pas comme si le cinéma était si différent d’une chaîne. Je comprendrais si on devait traiter des quantités physiques, telles que la pression, la température, l’énergie, où les unités diffèrent et certaines ne peuvent pas être négatives. L'auteur du blog doit essayer de fonctionner.Je suis d'accord avec l'auteur, principalement. S'il existe un comportement spécifique à un champ, comme la validation d'un identifiant de commande, je créerais une classe pour représenter ce type. Son deuxième point est encore plus important: si vous avez un ensemble de champs représentant un concept comme une adresse, créez une classe pour ce concept. Si vous programmez en Java, vous payez un prix élevé pour le typage statique. Vous pouvez aussi bien obtenir toute la valeur que vous pouvez.
la source
Ne le fais pas; ça compliquera trop les choses et vous n'en aurez pas besoin
... est la réponse que j'aurais écrit ici il y a 2 ans. Maintenant, cependant, je n'en suis pas si sûr. En fait, ces derniers mois, j'ai commencé à migrer l'ancien code vers ce format, non pas parce que je n'ai rien de mieux à faire, mais parce que j'en avais vraiment besoin pour implémenter de nouvelles fonctionnalités ou modifier les existantes. Je comprends les aversions automatiques des autres ici à voir ce code, mais je pense que c'est quelque chose qui mérite une réflexion sérieuse.
Avantages
L'un des principaux avantages est la possibilité de modifier et d'étendre le code. Si tu utilises
au lieu de simplement passer quelques entiers, ce qui est malheureusement le cas de nombreuses interfaces, il devient alors beaucoup plus facile d'ajouter ultérieurement une autre dimension. Ou changez le type en
double
. Si vous l'utilisezList<Author> authors
ou leList<Person> authors
remplaceList<String> authors
plus tard, il devient beaucoup plus facile d'ajouter plus d'informations à ce qu'un auteur représente. En écrivant cela comme cela, je me sens comme énonçant une évidence, mais en pratique, j’ai été coupable d’utiliser des cordes de cette façon plusieurs fois moi-même, surtout dans les cas où ce n’était pas évident au début, j’aurais besoin de plus de un string.J'essaie actuellement de refactoriser une liste de chaînes qui est entrelacée dans tout mon code car j'ai besoin de plus d'informations là-bas et je sens la douleur: \
Au-delà de cela, je suis d'accord avec l'auteur du blog pour dire qu'il contient davantage d'informations sémantiques , ce qui facilite la compréhension du lecteur. Bien que les paramètres reçoivent souvent des noms significatifs et une ligne de documentation dédiée, ce n'est souvent pas le cas avec les champs ou les sections locales.
Le dernier avantage est la sécurité du type , pour des raisons évidentes, mais à mes yeux, c'est une chose mineure ici.
Désavantages
Cela prend plus de temps pour écrire . Écrire une petite classe est rapide et facile, mais pas sans effort, surtout si vous avez besoin de beaucoup de ces classes. Si vous vous arrêtez toutes les 3 minutes pour écrire une nouvelle classe de wrapper, cela peut aussi nuire à votre concentration. J'aimerais cependant penser que cet effort ne se produira généralement qu'à la première étape de l'écriture d'un morceau de code; Je peux généralement avoir rapidement une bonne idée de ce que les entités devront impliquer.
Cela peut impliquer un grand nombre de setters (ou de constructions) et de getters redondants . L'auteur du blog donne l'exemple vraiment laid de
new Point(x(10), y(10))
au lieu denew Point(10, 10)
, et j'aimerais ajouter qu'un usage peut également impliquer des choses commeMath.max(p.x.get(), p.y.get())
au lieu deMath.max(p.x, p.y)
. Et le code long est souvent considéré comme plus difficile à lire, et à juste titre. Mais en toute honnêteté, j'ai l'impression que beaucoup de code déplace des objets, et que seules des méthodes choisies le créent, et encore moins ont besoin d'accéder à ses détails précis (ce qui n'est pas OOPy de toute façon).Discutable
Je dirais que cela aide ou non à la lisibilité du code est discutable. Oui, plus d'informations sémantiques, mais un code plus long. Oui, il est plus facile de comprendre le rôle de chaque section locale, mais il est plus difficile de comprendre ce que vous pouvez en faire si vous ne lisez pas sa documentation.
Comme avec la plupart des écoles de pensée de programmation, je pense qu'il est malsain de pousser celui-ci à l'extrême. Je ne me vois jamais séparer les coordonnées x et y d'un type différent. Je ne pense pas que
Count
nécessaire quand unint
devrait suffire. Je n'aime pas l'unsigned int
utilisation en C - bien que théoriquement bon, cela ne vous donne tout simplement pas assez d'informations, et cela interdit l'extension ultérieure de votre code pour prendre en charge cette valeur magique -1. Parfois, vous avez besoin de simplicité.Je pense que ce blog est un peu extrême. Mais dans l’ensemble, j’ai appris d’une expérience douloureuse que l’idée de base est faite à partir des éléments appropriés.
J'ai une profonde aversion pour le code trop élaboré. Je le fais vraiment. Mais bien utilisé, je ne pense pas que cette sur-ingénierie.
la source
Bien que ce soit un peu excessif, je pense souvent que la plupart des éléments que j'ai vus sont plutôt sous-développés.
Il ne s’agit pas uniquement de "sécurité". L’un des avantages de Java est qu’il vous aide beaucoup à vous rappeler et à déterminer exactement ce dont une méthode de bibliothèque a besoin / attend.
La bibliothèque Java TRÈS (de loin) avec laquelle j'ai travaillé a été écrite par une personne qui aimait beaucoup Smalltalk et qui a modelé la bibliothèque GUI d'après elle pour qu'elle ressemble davantage à smalltalk - le problème étant que chaque méthode prenait le même objet de base, mais ne pouvait pas réellement utiliser tout ce sur quoi l'objet de base pouvait être transtypé. Vous deviez donc de nouveau deviner ce qu'il fallait passer dans les méthodes et ne pas savoir si vous aviez échoué jusqu'au moment de l'exécution (Quelque chose que j'avais l'habitude de gérer à chaque fois travaillait en C).
Un autre problème - Si vous passez des chaînes, des ints, des collections et des tableaux sans objets, tout ce que vous avez, ce sont des boules de données sans signification. Cela semble naturel lorsque vous pensez en termes de bibliothèques que "certaines applications" vont utiliser, mais lors de la conception d'une application complète, il est beaucoup plus utile d'attribuer une signification (code) à toutes vos données à l'endroit où les données sont définies et en y réfléchissant uniquement. termes de la façon dont ces objets de haut niveau interagissent. Si vous transmettez des primitives plutôt que des objets, vous modifiez (par définition) des données dans un endroit différent de celui défini (c’est aussi pourquoi je n’aime vraiment pas les Setters et les Getters - le même concept, vous utilisez sur des données qui ne vous appartiennent pas).
Enfin, si vous définissez des objets distincts pour tout, vous avez toujours la possibilité de tout valider - par exemple, si vous créez un objet avec un code postal et que vous constatez ensuite que vous devez vous assurer que le code postal comprend toujours l’extension à 4 chiffres que vous avez. endroit parfait pour le mettre.
Ce n'est pas vraiment une mauvaise idée. En y réfléchissant, je ne suis même pas sûr que je dirais qu'il a été surinformé du tout, il est simplement plus facile de travailler avec à peu près tout, à la seule exception de la prolifération de petites classes, mais les classes Java sont si légères et faciles à utiliser. d’écrire que c’est à peine un coût (ils peuvent même être générés).
Je serais vraiment intéressé de voir un projet Java bien écrit qui définit trop de classes (où cela rend plus difficile la programmation), je commence à penser qu'il n'est pas possible d'avoir trop de classes.
la source
Je pense que vous devez regarder ce concept d'un autre point de départ. Regardez du point de vue d’un concepteur de base de données: les types passés dans le premier exemple ne définissent pas vos paramètres de manière unique, encore moins utile.
Deux paramètres sont nécessaires pour spécifier le client réel qui réserve les billets, vous pouvez avoir deux films différents avec des noms identiques (par exemple, des remakes), vous pouvez avoir le même film avec des noms différents (par exemple, des traductions). Une certaine chaîne de salles de cinéma pourrait avoir des bureaux différents, alors comment allez - vous traiter que dans une chaîne et d'une manière cohérente (par exemple , vous utilisez
$chain ($city)
ou$chain in $city
ou même quelque chose d' autre et comment allez - vous pour vous assurer que ce Le pire est en fait de spécifier votre client au moyen de deux paramètres, le fait qu’un prénom et un nom de famille soient fournis ne garantit pas un client valide (et vous ne pouvez pas distinguer deux personnesJohn Doe
).La réponse à cette question est de déclarer les types, mais ceux-ci seront rarement des enveloppes minces comme je le montre ci-dessus. Très probablement, ils fonctionneront comme votre stockage de données ou seront couplés avec une sorte de base de données. Ainsi, un
Cinema
objet aura probablement un nom, un emplacement, ... et ainsi vous vous débarrasserez de telles ambiguïtés. Si ce sont des emballages minces, ils le sont par coïncidence.Ainsi, à mon humble avis, l’article de blog dit simplement "assurez-vous de passer les types corrects", son auteur vient de faire le choix trop contraignant de choisir des types de données de base en particulier (ce qui est un message erroné).
L'alternative proposée est meilleure:
D'autre part, je pense que l'article de blog va trop loin en emballant tout.
Count
est trop générique, je pourrais compter des pommes ou des oranges avec cela, les ajouter et avoir toujours une situation entre mes mains où le système de typographie me permet de faire des opérations absurdes. Vous pouvez bien sûr appliquer la même logique que dans le blog et définir des types,CountOfOranges
etc., mais c’est aussi ridicule.Pour ce que ça vaut, j'écrirais en fait quelque chose comme
Longue histoire courte: vous ne devriez pas passer des variables absurdes; les seules fois où vous spécifiez réellement un objet avec une valeur qui ne détermine pas l'objet réel, sont lorsque vous exécutez une requête (par exemple
public Collection<Film> findFilmsWithTitle(String title)
) ou lorsque vous créez une preuve de concept. Gardez votre système de caractères propre, évitez donc d'utiliser un type trop général (par exemple, un film représenté par unString
) ou trop restrictif / spécifique / artificiel (Count
au lieu deint
). Utilisez un type qui définit votre objet de manière unique et non ambiguë chaque fois que cela est possible et viable.edit : un résumé encore plus court. Pour les petites applications (par exemple, preuve de concept): pourquoi s'embarrasser d'un design compliqué? Il suffit d'utiliser
String
ouint
et passer à autre chose.Pour les grandes applications: est-il vraiment probable que vous ayez beaucoup de classes consistant en un seul champ avec un type de données de base? Si vous avez peu de telles classes, vous avez juste des objets "normaux", rien de spécial ne s'y passe.
Je pense que l’idée d’encapsuler des chaînes,… n’est qu’une conception incomplète: trop compliqué pour les petites applications, pas assez complet pour les grandes applications.
la source
Pour moi, cela revient à utiliser des régions en C #. Généralement, si vous estimez que cela est nécessaire pour rendre votre code lisible, vous aurez de plus gros problèmes sur lesquels vous devriez passer votre temps.
la source
Je dirais que c'est une très bonne idée dans une langue avec une fonctionnalité typedef fortement typée.
En Java, ce n’est pas le cas. La création d’une toute nouvelle classe pour ces choses signifie que le coût est probablement supérieur au bénéfice. Vous pouvez également obtenir 80% des bénéfices en faisant attention à la dénomination de vos variables / paramètres.
la source
Ce serait bien, IF String (fin Integer, et ... ne parlant que de String) n'étant pas définitif, ces classes pourraient donc être une chaîne (restreinte) ayant une signification, tout en pouvant être envoyées à un objet indépendant qui sait comment gérer le type de base (sans converser d'avant en arrière).
Et "goodnes" de celui-ci augmentent quand il y a par exemple. restrictions sur tous les noms.
Mais lors de la création d'une application (pas de bibliothèque), il est toujours possible de la refactoriser. Je préfère donc commencer sans elle.
la source
Pour donner un exemple: dans notre système actuellement développé, de nombreuses entités différentes peuvent être identifiées par plusieurs types d'identificateurs (en raison de l'utilisation de systèmes externes), parfois même du même type d'entité. Tous les identifiants sont des chaînes. Ainsi, si quelqu'un spécifie quel type d'identifiant doit être passé en paramètre, aucune erreur de compilation ne s'affiche, mais le programme explose au moment de l'exécution. Cela arrive assez souvent. Je dois donc dire que l'objectif principal de ce principe n'est pas de protéger contre le changement (même s'il sert également à cela), mais de nous protéger contre les insectes. Et de toute façon, si quelqu'un conçoit une API, elle doit refléter les concepts du domaine. Il est donc utile d'un point de vue conceptuel de définir des classes spécifiques à un domaine - tout dépend de la question de savoir s'il y a un ordre dans l'esprit des développeurs,
la source