Quelle est la meilleure pratique pour la validation des paramètres de constructeur?
Supposons un simple morceau de C #:
public class MyClass
{
public MyClass(string text)
{
if (String.IsNullOrEmpty(text))
throw new ArgumentException("Text cannot be empty");
// continue with normal construction
}
}
Serait-il acceptable de lancer une exception?
L'alternative que j'ai rencontrée était la pré-validation, avant d'instancier:
public class CallingClass
{
public MyClass MakeMyClass(string text)
{
if (String.IsNullOrEmpty(text))
{
MessageBox.Show("Text cannot be empty");
return null;
}
else
{
return new MyClass(text);
}
}
}
c#
validation
constructors
MPelletier
la source
la source
Réponses:
J'ai tendance à effectuer toute ma validation dans le constructeur. C'est indispensable car je crée presque toujours des objets immuables. Pour votre cas spécifique, je pense que cela est acceptable.
Si vous utilisez .NET 4, vous pouvez le faire. Bien entendu, cela dépend si vous considérez qu'une chaîne ne contenant que des espaces n'est pas valide.
la source
Init
pour recevoir une sorte de code d'erreur lorsqu'une exception fonctionnera aussi bien et forcer votre objet à toujours conserver un état valide?ArgumentNullException
et une autre qui vérifie si vide et jette unArgumentException
. Permet à l'appelant d'être plus difficile s'il le souhaite lors de la gestion des exceptions.Beaucoup de gens disent que les constructeurs ne devraient pas lancer d'exceptions. KyleG sur cette page , par exemple, fait justement cela. Honnêtement, je ne peux pas penser à une raison pour laquelle pas.
En C ++, le lancement d'une exception à partir d'un constructeur est une mauvaise idée car cela vous laisse avec de la mémoire allouée contenant un objet non initialisé auquel vous n'avez aucune référence (c'est-à-dire qu'il s'agit d'une fuite de mémoire classique). C’est probablement de là que vient la stigmatisation: un groupe de développeurs C ++ de la vieille école ont mis en demi-arrière leur apprentissage de C # et ont simplement appliqué ce qu’ils savaient du C ++. En revanche, dans Objective-C, Apple a séparé l’étape d’allocation de l’étape d’initialisation afin que les constructeurs de cette langue puissent générer des exceptions.
C # ne peut pas perdre de la mémoire d'un appel infructueux à un constructeur. Même certaines classes du .NET Framework jetteront des exceptions dans leurs constructeurs.
la source
Lance une exception si la classe ne peut pas être mise dans un état cohérent en ce qui concerne son utilisation sémantique. Sinon ne le faites pas. N'autorisez JAMAIS un objet à exister dans un état incohérent. Cela implique de ne pas fournir de constructeurs complets (comme avoir un constructeur vide + initialize () avant que votre objet ne soit réellement construit) ... JUST SAY NO!
À la limite, tout le monde le fait. Je l'ai fait l'autre jour pour un objet très étroitement utilisé dans un périmètre étroit. Un jour ou l'autre, quelqu'un ou moi-même paierons probablement le prix de ce feuillet dans la pratique.
Je devrais noter que par "constructeur" je veux dire la chose que le client appelle pour construire l'objet. Cela pourrait tout aussi bien être autre chose qu'une construction réelle appelée "Constructeur". Par exemple, quelque chose comme cela en C ++ ne violerait pas le principe IMNSHO:
Comme le seul moyen de créer un
funky_object
est d'appelerbuild_funky
le principe de ne jamais permettre à un objet invalide d'exister reste intact même si le "constructeur" lui-même ne termine pas le travail.Cela représente beaucoup de travail supplémentaire pour un gain discutable (peut-être même une perte). Je préférerais toujours la route d'exception.
la source
Dans ce cas, j'utiliserais la méthode de l'usine. Fondamentalement, votre classe a uniquement des constructeurs privés et une méthode de fabrique qui renvoie une instance de votre objet. Si les paramètres initiaux ne sont pas valides, il suffit de renvoyer null et de laisser le code appelant décider quoi faire.
Ne jamais lancer d'exceptions à moins que les conditions ne soient exceptionnelles. Je pense que dans ce cas, une valeur vide peut être passée facilement. Si tel est le cas, l'utilisation de ce modèle éviterait les exceptions et les pénalités de performance qu'elles entraînent tout en maintenant l'état MyClass valide.
la source
On pourrait raisonnablement remettre en question ces directives et dire: "Mais je ne respecte pas ces règles et mon code fonctionne bien!" À cela, je répondrais: "Cela peut être vrai jusqu'à ce que ce ne soit pas le cas."
la source
Cela dépend de ce que fait MyClass. Si MyClass était en réalité une classe de référentiel de données et que le paramètre text était une chaîne de connexion, il est recommandé de générer une exception ArgumentException. Toutefois, si MyClass est la classe StringBuilder (par exemple), vous pouvez simplement la laisser vide.
Cela dépend donc de la pertinence du texte du paramètre pour la méthode - l'objet a-t-il un sens avec une valeur nulle ou vide?
la source
Ma préférence est de définir une valeur par défaut, mais je sais que Java a la bibliothèque "Apache Commons" qui fait quelque chose comme cela, et je pense que c'est aussi une assez bonne idée. Je ne vois pas d'inconvénient à lancer une exception si la valeur non valide placerait l'objet dans un état inutilisable. une ficelle n'est pas un bon exemple, mais que se passerait-il si c'était pour le DI du pauvre? Vous ne pourriez pas fonctionner si une valeur de
null
était passée à la place d'uneICustomerRepository
interface, par exemple. Dans une situation comme celle-ci, lancer une exception est la bonne façon de gérer les choses, dirais-je.la source
Quand j’avais l’habitude de travailler en c ++, je n’avais pas pour habitude de lancer une exception dans le constructeur à cause d’un problème de fuite de mémoire qu’elle pouvait causer, j’avais appris à la dure. Quiconque travaille avec c ++ sait à quel point une fuite de mémoire peut être difficile et problématique.
Mais si vous êtes en c # / Java, vous n’avez pas ce problème, car le garbage collector va collecter la mémoire. Si vous utilisez C #, je pense qu'il est préférable d'utiliser property pour obtenir un moyen agréable et cohérent de s'assurer que les contraintes de données sont garanties.
la source
Lancer une exception sera une vraie douleur pour les utilisateurs de votre classe. Si la validation pose problème, la création de l'objet est généralement effectuée en 2 étapes.
1 - Créer l'instance
2 - Appelez une méthode Initialize avec un résultat.
OU
Appelez une méthode / fonction qui crée la classe pour vous et qui peut effectuer la validation.
OU
Ayez un drapeau isValid, ce que je n'aime pas particulièrement mais que certaines personnes aiment bien.
la source
isValid = false
. Avoir à tester après la création d’une classe s’il est valide est une conception horrible et très sujette aux erreurs. Et, vous avez dit, avoir un constructeur, puis appeler initialiser ... Et si je n'appelle pas initialiser? Quoi alors? Et si Initialize obtenait de mauvaises données, puis-je lancer une exception maintenant? Votre classe ne devrait pas être difficile à utiliser.