Est-ce une «odeur de motif» de mettre des getters comme «FullName» ou «FormattedPhoneNumber» dans votre modèle?

13

Je travaille sur une application ASP.NET MVC, et j'ai pris l'habitude de mettre ce qui semble être des getters utiles et pratiques dans mes classes de modèle / entité.

Par exemple:

public class Member
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PhoneNumber { get; set; }

    public string FullName
    {
        get { return FirstName + " " + LastName; }
    }

    public string FormattedPhoneNumber
    {
        get { return "(" + PhoneNumber.Substring(0, 3) + ") " + PhoneNumber.Substring(3, 3) + "-" + PhoneNumber.Substring(6); }
    }
}

Je me demande si les gens pensent aux FullNameet aux FormattedPhoneNumbergetters.

Ils facilitent la création de formats de données standardisés dans l'application et semblent économiser beaucoup de code répété, mais on pourrait certainement affirmer que le format de données est quelque chose qui devrait être géré lors du mappage d'un modèle à un modèle de vue.

En fait, à l'origine, j'appliquais ces formats de données dans ma couche de service où je fais mon mappage, mais cela devenait un fardeau d'avoir constamment à écrire des formateurs, puis à les appliquer à de nombreux endroits différents. Par exemple, j'utilise "Nom complet" dans la plupart des vues, et avoir à taper quelque chose comme model.FullName = MappingUtilities.GetFullName(entity.FirstName, entity.LastName);partout semblait beaucoup moins élégant que de simplement taper model.FullName = entity.FullName(ou, si vous utilisez quelque chose comme AutoMapper, potentiellement ne rien taper du tout).

Alors, où tracez-vous la ligne en ce qui concerne le formatage des données. Est-il «correct» de mettre en forme les données dans votre modèle ou est-ce une «odeur de motif»?

Remarque: je n'ai certainement pas de code HTML dans mon modèle. J'utilise des aides html pour cela. Je parle strictement de formatage ou de combinaison de données (et en particulier de données fréquemment utilisées).

devuxer
la source
1
Cela peut être pratique. Mais espérez et priez pour que vous n'ayez jamais à internationaliser ce code.
btilly
@btilly, bon point, mais je suis sûr à 99,99% que je ne le ferai pas.
devuxer
Spécifique à FullName et PhoneNumber, certainement. La question concerne-t-elle spécifiquement ceux-ci parce qu'ils ont des formats incohérents dans différentes cultures, ou @DanM a-t-il simplement choisi des exemples qui ne s'internationalisent pas bien pour une question plus générale?
Greg Jackson
@Greg Jackson, certainement l'échelle. Comme l'a souligné ammoQ, PhoneNumberappartient probablement à sa propre classe (que j'ai maintenant implémentée). Mais FullNamec'est vraiment celui qui m'a motivé à écrire la question. Mais je suis intéressé à savoir si, en général, il est judicieux de mettre en forme / peigner les données, etc. dans le modèle pour les choses qui s'appliqueront à l'échelle de l'application. D'après les réponses ci-dessous, il semble que ce ne soit pas un anti-modèle, mais la décision doit être prise avec soin.
devuxer
Gardez à l'esprit que cette mise en forme spécifique pour le nom complet peut également ne pas bien s'internationaliser. En Asie orientale, par exemple, l'ordre des noms est inversé par rapport à ce qu'il est dans le monde occidental. Avez-vous vraiment besoin de gérer explicitement cela? Peut-être pas, mais gardez à l'esprit qu'il y a beaucoup de choses délicates que vous rencontrerez lors du formatage des données.
Greg Jackson

Réponses:

9

Dans votre exemple, j'aime le FullNamegetter (pour toutes les raisons que vous avez données) mais je n'aime pas le getter FormattedPhoneNumber. La raison en est: ce n'est probablement pas si facile (une fois que vous avez des numéros de téléphone internationaux, etc.) et si vous placez la logique de formatage des numéros de téléphone dans une méthode de Member, vous aurez probablement besoin de refactoriser (ou copier-coller caugh ) une fois que vous besoin d'un numéro de téléphone formaté pour Institution, Vendoretc. aussi.

EDIT: OMI, il serait préférable d'avoir une PhoneNumberclasse avec un Formattedgetter.

user281377
la source
+1 et merci. Mais que se passe-t-il si je mets réellement mon code de mise en forme de numéro de téléphone dans une méthode d'extension? Ensuite, n'importe quel modèle pourrait l'utiliser, et il n'y aurait pas de refactoring ou de copier / coller. Cette solution serait encore beaucoup moins répétitive que l'application du formateur à chaque numéro de téléphone qui apparaît dans chaque vue.
devuxer
3
Utiliser une méthode d'extension pour cela serait un moyen rapide pour l'antipattern "décomposition fonctionnelle". En utilisant une méthode d'extension (pour la Stringclasse, je suppose), vous "apprenez" à la Stringclasse comment formater les numéros de téléphone. Est-ce vraiment la responsabilité de la Stringclasse de connaître les numéros de téléphone? Je ne pense pas. Utilisées comme ça, les méthodes d'extension sont du sucre syntaxique pour laisser quelque chose qui n'est clairement pas orienté objet ressembler à ce qu'il était.
user281377
1
D'accord, oublie que j'ai dit la méthode d'extension. Imaginez que j'ai dit méthode utilitaire ou classe de formateur. Je dis simplement que le refactoring / copier-coller ne devrait pas être nécessaire, que j'aie un getter de modèle ou que je fasse le formatage ailleurs.
devuxer
1
Et j'aime l'idée d'une PhoneNumberclasse. J'ai prévu de le faire de toute façon parce que j'ai aussi une PhoneTypepropriété.
devuxer
Je n'aime pas l'idée d'avoir PhoneNumberune classe d'instance car les données sont natives string. Il doit plutôt s'agir d'une classe statique avec des méthodes comme public static string Format(string phoneNumber, PhoneNumberStyle style).
M. Anderson
5

Les choses que vous devez considérer lors de l'écriture de code: est-ce correct? Est-il lisible? Est-ce efficace? Est-il maintenable? Je dirais, comme @btilly l'a mentionné, que ce n'est pas maintenable en raison du formatage spécifique à la culture, mais la question semble être plus générale que cela.

L'utilisation d'accesseurs comme ceux-ci rend votre code plus lisible et, selon la façon dont vous l'utilisez, peut rendre les autres parties de votre code beaucoup plus propres. À mon avis, cela ne sent pas du tout. Cela commencerait à sentir si vous aviez des accesseurs de formatage pour tout type de chaîne que vous pourriez vouloir imprimer ( public string FirstLastName; public string FullName; public string FullNameWithMiddleInitial; public string PhoneNumberWithAreaCode; public string PhoneNumberWithoutAreaCode; public string PhoneNumberWithCountryCode;, etc.)

Ou, pour le dire autrement, l'utilisation d'un motif ne fait pas automatiquement que votre code ait une "odeur de motif". Vous devez en abuser si vous voulez gagner cet attribut.

Greg Jackson
la source
Merci, Greg. +1. Je suis d'accord avec vous pour mettre chaque combinaison. J'essaie vraiment de trouver le moyen le plus propre de normaliser la façon dont les données sont affichées.
devuxer
3

Brise le principe de la responsabilité unique. Pourquoi ne pas faire une classe de numéro de téléphone, etc ...?

Edward Strange
la source
D'accord, je suis en fait d'accord avec cela (voir la discussion sous la réponse de ammoQ), mais devrais-je également faire un FullNamecours?
devuxer
Oui, vous devez, mais vous devez corriger l'orthographe de "FullName" à "FoolName". Ou peut-être "PersonalName", car il s'agit du nom d'une personne, et non d'un nom complet.
kevin cline
1
Vraiment? À moins que la classe donnée ne croisse, qu'est-ce qui ne va pas exactement? Même si elle croît, à quel point serait-il difficile de refacturer alors?
Job
@DanM: Alors c'est ce que vous demandez, c'est-à-dire leur demander de taper leur nom légal complet. Si vous triez par prénom, vous triez vraiment sur la première lettre (puis la deuxième, etc.) du prénom (puis sur la suivante, etc.), de sorte que les noms seront triés de la même manière.
Matt Ellen
1

Pour votre exemple, je ne pense pas que ce soit trop important pour utiliser des formats spécifiques. C'est une ou deux et toutes les parties de l'application utilisent le même format.

Lorsque cette décision commencerait à s'effondrer, c'est lorsque vous avez les mêmes données à plusieurs endroits différents nécessitant des formats différents .

Si cela arrivait, je serais tenté de Memberramener la classe à:

public class Member
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PhoneNumber { get; set; }  
}

Et puis faites des adaptateurs différents pour chaque cible. Par exemple, supposons que les informations étaient requises au format CSV:

public static class CSVMemberAdapter
{
    public static string ToCSV(this Member mbr)
    {
         return mbr.Id + "," + mbr.LastName + "," + mbr.FirstName, "," mbr.PhoneNumber;
    }
}

En supposant toujours que vous avez nettoyé les données afin qu'aucune virgule, etc. ne soit dans les chaînes.

L'adaptateur ne doit pas être une méthode d'extension, mais pour ce cas imaginaire, il semble convenir.

Peter K.
la source
Cela fait un moment que je n'ai pas écrit C #, mais il y a sûrement des classes de sérialisation qui peuvent gérer cela, au lieu d'écrire fastidieusement des méthodes comme toCSV encore et encore et encore et encore et encore et encore et encore et encore et encore et encore et encore et plus et plus et plus et plus et plus ...
kevin Cline
@kevin cline: Cela dépend de ce dont vous avez besoin à chaque évier. Si tout ce dont ils ont besoin est du XML sérialisé, très bien. De nombreux systèmes ne le font pas. Si cela doit être fait overautant de fois que cela, alors il y a un problème avec la conception.
Peter K.
il y a généralement de nombreuses classes à sérialiser. Il devrait être possible d'écrire un seul CSV ou un autre sérialiseur qui pourrait gérer la plupart des classes par réflexion, plutôt que de les coder à la main comme votre exemple.
kevin cline
@kevin cline: Violemment d'accord! J'écrivais juste quelque chose de très spécifique pour la question posée. Des exemples concrets et simples tendent à mieux expliquer les choses.
Peter K.