En Java et C #, vous pouvez créer un objet avec des propriétés qui peuvent être définies lors de l'initialisation en définissant un constructeur avec des paramètres, en définissant chaque propriété après la construction de l'objet ou en utilisant le modèle d'interface générateur / fluide. Cependant, C # 3 a introduit les initialiseurs d'objets et de collections, ce qui signifie que le modèle de générateur était largement inutile. Dans un langage sans initialiseurs, on pourrait implémenter un constructeur puis l'utiliser comme ceci:
Vehicle v = new Vehicle.Builder()
.manufacturer("Toyota")
.model("Camry")
.year(1997)
.colour(CarColours.Red)
.addSpecialFeature(new Feature.CDPlayer())
.addSpecialFeature(new Feature.SeatWarmer(4))
.build();
Inversement, en C # on pourrait écrire:
var vehicle = new Vehicle {
Manufacturer = "Toyota",
Model = "Camry",
Year = 1997,
Colour = CarColours.Red,
SpecialFeatures = new List<SpecialFeature> {
new Feature.CDPlayer(),
new Feature.SeatWarmer { Seats = 4 }
}
}
... éliminant le besoin d'un constructeur, comme le montre l'exemple précédent.
Sur la base de ces exemples, les générateurs sont-ils toujours utiles en C #, ou ont-ils été entièrement remplacés par les initialiseurs?
are builders useful
pourquoi je continue à voir ce genre de questions? Un générateur est un modèle de conception - bien sûr, il peut être exposé en tant que fonctionnalité de langage, mais en fin de compte, ce n'est qu'un modèle de conception. Un motif de conception ne peut pas être "faux". Il peut ou non répondre à votre cas d'utilisation, mais cela ne rend pas le modèle entier incorrect, juste son application. N'oubliez pas qu'à la fin de la journée, les modèles de conception résolvent des problèmes particuliers - si vous ne rencontrez pas le problème, alors pourquoi essayez-vous de le résoudre?Réponses:
Comme évoqué par @ user248215, le vrai problème est l'immuabilité. La raison pour laquelle vous utiliseriez un générateur en C # serait de maintenir l'explicitation d'un initialiseur sans avoir à exposer les propriétés réglables. Ce n'est pas une question d'encapsulation, c'est pourquoi j'ai écrit ma propre réponse. L'encapsulation est assez orthogonale car invoquer un setter n'implique pas ce que le setter fait réellement ni ne vous lie à son implémentation.
La prochaine version de C #, 8.0, est susceptible d'introduire un
with
mot - clé qui permettra aux objets immuables d'être initialisés de manière claire et concise sans avoir besoin d'écrire des constructeurs.Une autre chose intéressante que vous pouvez faire avec les constructeurs, par opposition aux initialiseurs, est qu'ils peuvent entraîner différents types d'objets en fonction de la séquence de méthodes appelées.
Par exemple
Pour clarifier l'exemple ci-dessus, il s'agit d'une bibliothèque de correspondance de modèles qui utilise le modèle de générateur pour créer une "correspondance" en spécifiant des cas. Les cas sont ajoutés en appelant la
Case
méthode en lui passant une fonction. Sivalue
est affectable au type de paramètre de la fonction, il est appelé. Vous pouvez trouver le code source complet sur GitHub et, comme les commentaires XML sont difficiles à lire en texte brut, voici un lien vers la documentation construite SandCastle (voir la section Remarques )la source
IEnumerable<Func<T, TResult>>
membre.TResult
. En fin de compte, l'inférence de type y était pour beaucoup. Je voulais aussi qu'elle "ressemble" à une structure de contrôle. De plus, je ne voulais pas utiliser d'initialisation car cela permettrait une mutation.Les initialiseurs d'objets nécessitent que les propriétés soient accessibles par le code appelant. Les constructeurs imbriqués peuvent accéder aux membres privés de la classe.
Si vous vouliez rendre
Vehicle
immuable (en rendant tous les setters privés), alors le générateur imbriqué pourrait être utilisé pour définir les variables privées.la source
Ils servent tous à des fins différentes !!!
Les constructeurs peuvent initialiser les champs marqués
readonly
ainsi que les membres privés et protégés. Cependant, vous êtes quelque peu limité dans ce que vous pouvez faire à l'intérieur d'un constructeur; vous devez éviter de passerthis
à des méthodes externes, par exemple, et vous devez éviter d'appeler des membres virtuels, car ils peuvent s'exécuter dans le contexte d'une classe dérivée qui ne s'est pas encore construite. De plus, les constructeurs sont garantis pour fonctionner (à moins que l'appelant ne fasse quelque chose de très inhabituel), donc si vous définissez des champs dans le constructeur, le code dans le reste de la classe peut supposer que ces champs ne seront pas nuls.Les initialiseurs fonctionnent après la construction. Ils vous permettent uniquement d'appeler des propriétés publiques; les champs privés et / ou en lecture seule ne peuvent pas être définis. Par convention, l'acte de définir une propriété doit être assez limité, par exemple, il doit être idempotent et avoir des effets secondaires limités.
Les méthodes de générateur sont des méthodes réelles et permettent donc plus d'un argument, et par convention peuvent avoir des effets secondaires, y compris la création d'objets. Bien qu'ils ne puissent pas définir de champs en lecture seule, ils peuvent à peu près faire autre chose. De plus, les méthodes peuvent être implémentées en tant que méthodes d'extension (comme presque toutes les fonctionnalités de LINQ).
la source