Récemment, j'ai pensé à sécuriser une partie de mon code. Je suis curieux de savoir comment on peut s'assurer qu'un objet ne peut jamais être créé directement, mais uniquement via une méthode d'une classe d'usine. Disons que j'ai une classe "objet métier" et que je veux m'assurer que toute instance de cette classe aura un état interne valide. Pour ce faire, je devrai effectuer quelques vérifications avant de créer un objet, probablement dans son constructeur. Tout va bien jusqu'à ce que je décide que je souhaite que cette vérification fasse partie de la logique métier. Alors, comment puis-je faire en sorte qu'un objet métier puisse être créé uniquement via une méthode de ma classe de logique métier, mais jamais directement? Le premier désir naturel d'utiliser un bon vieux mot-clé "ami" de C ++ échouera avec C #. Nous avons donc besoin d'autres options ...
Essayons un exemple:
public MyBusinessObjectClass
{
public string MyProperty { get; private set; }
public MyBusinessObjectClass (string myProperty)
{
MyProperty = myProperty;
}
}
public MyBusinessLogicClass
{
public MyBusinessObjectClass CreateBusinessObject (string myProperty)
{
// Perform some check on myProperty
if (true /* check is okay */)
return new MyBusinessObjectClass (myProperty);
return null;
}
}
Tout va bien jusqu'à ce que vous vous souveniez que vous pouvez toujours créer une instance MyBusinessObjectClass directement, sans vérifier l'entrée. Je voudrais exclure complètement cette possibilité technique.
Alors, qu'en pense la communauté?
la source
CreateBusinessObject
méthode est de valider les arguments AND to (retourner un nouvel objet valide OU renvoyer une erreur) dans un seul appel de méthode lorsqu'un appelant ne sait pas immédiatement si les arguments sont valides pour passer dans le constructeur.Distance
instance avec un argument négatif devrait lancer unArgumentOutOfRangeException
par exemple, vous ne gagneriez rien à créer unDistanceFactory
qui effectue la même vérification. Je ne vois toujours pas ce que vous avez à gagner ici.Réponses:
Il semble que vous souhaitiez simplement exécuter une logique métier avant de créer l'objet - alors pourquoi ne créez-vous pas simplement une méthode statique à l'intérieur de la "BusinessClass" qui fait tout le travail de vérification sale de "myProperty" et rend le constructeur privé?
L'appeler serait assez simple:
la source
Vous pouvez rendre le constructeur privé et la fabrique un type imbriqué:
Cela fonctionne car les types imbriqués ont accès aux membres privés de leurs types englobants. Je sais que c'est un peu restrictif, mais j'espère que cela aidera ...
la source
CreateBusinessObject
méthode à l'intérieur d'uneFactory
classe imbriquée au lieu d'avoir cette méthode statique comme une méthode directement de laBusinessObject
classe ... pouvez-vous vous rappeler votre motivation pour le faire?Build
.)Ou, si vous voulez être vraiment sophistiqué, inversez le contrôle: demandez à la classe de renvoyer l'usine et d'instrumenter l'usine avec un délégué qui peut créer la classe.
:)
la source
Vous pouvez rendre le constructeur de votre classe MyBusinessObjectClass interne et le déplacer ainsi que la fabrique dans leur propre assembly. Désormais, seule la fabrique devrait être capable de construire une instance de la classe.
la source
En dehors de ce que Jon a suggéré, vous pouvez également faire en sorte que la méthode de fabrique (y compris la vérification) soit une méthode statique de BusinessObject en premier lieu. Ensuite, faites en sorte que le constructeur soit privé et tout le monde sera obligé d'utiliser la méthode statique.
Mais la vraie question est: pourquoi avez-vous cette exigence? Est-il acceptable de déplacer l'usine ou la méthode d'usine dans la classe?
la source
Après tant d'années, cela a été demandé, et toutes les réponses que je vois vous disent malheureusement comment vous devez faire votre code au lieu de donner une réponse directe. La vraie réponse que vous recherchiez est d'avoir vos classes avec un constructeur privé mais un instanciateur public, ce qui signifie que vous ne pouvez créer de nouvelles instances qu'à partir d'autres instances existantes ... qui ne sont disponibles que dans l'usine:
L'interface de vos cours:
Ta classe:
Et, enfin, l'usine:
Ensuite, vous pouvez facilement modifier ce code si vous avez besoin de paramètres supplémentaires pour l'instanciation ou pour prétraiter les instances que vous créez. Et ce code vous permettra de forcer l'instanciation à travers la fabrique car le constructeur de classe est privé.
la source
Une autre option (légère) consiste à créer une méthode de fabrique statique dans la classe BusinessObject et à garder le constructeur privé.
la source
Donc, il semble que ce que je veux ne peut pas être fait de manière «pure». C'est toujours une sorte de "rappel" à la classe logique.
Peut-être que je pourrais le faire d'une manière simple, il suffit de faire une méthode de contructor dans la classe d'objet d'abord appeler la classe logique pour vérifier l'entrée?
De cette façon, l'objet métier ne peut pas être créé directement et la méthode de vérification publique dans la logique métier ne fera aucun mal non plus.
la source
Dans un cas de bonne séparation entre les interfaces et les implémentations, le
modèle protected-constructor-public-initializer permet une solution très soignée.
Étant donné un objet métier:
et une usine commerciale:
la modification suivante de l'
BusinessObject.New()
initialiseur donne la solution:Ici, une référence à une usine commerciale concrète est nécessaire pour appeler l'
BusinessObject.New()
initialiseur. Mais le seul qui dispose de la référence requise est l'usine commerciale elle-même.Nous avons obtenu ce que nous voulions: le seul qui puisse créer l'
BusinessObject
estBusinessFactory
.la source
public static IBusinessObject New(BusinessFactory factory)
cela soulèveront de nombreux sourcils si quelqu'un d'autre maintient le code.factory
enasFriend
. Dans ma base de code, il apparaîtrait alors commepublic static IBusinessObject New(BusinessFactory asFriend)
la source
Je mettrais l'usine dans le même assemblage que la classe de domaine et marquerais le constructeur de la classe de domaine comme interne. De cette façon, n'importe quelle classe de votre domaine peut être en mesure de créer une instance, mais vous ne vous faites pas confiance, n'est-ce pas? Quiconque écrit du code en dehors de la couche de domaine devra utiliser votre usine.
Cependant, je dois remettre en question votre approche :-)
Je pense que si vous voulez que votre classe Person soit valide lors de la création, vous devez mettre le code dans le constructeur.
la source
Cette solution est basée sur l' idée courante d'utiliser un jeton dans le constructeur. Fait dans cette réponse, assurez-vous que l'objet créé uniquement par l'usine (C #)
la source
De multiples approches avec différents compromis ont été mentionnées.
Create
méthode et un ctor privé.Je voudrais proposer la fabrique comme une classe partielle qui contient des classes imbriquées privées avec des constructeurs publics. Vous cachez à 100% l'objet que votre usine est en train de construire et n'exposez que ce que vous choisissez via une ou plusieurs interfaces.
Le cas d'utilisation que j'ai entendu pour cela serait lorsque vous souhaitez suivre 100% des instances dans l'usine. Cette conception ne garantit à personne d'autre que l'usine d'avoir accès à la création d'instances de «produits chimiques» définis dans «l'usine» et élimine le besoin d'un assemblage séparé pour y parvenir.
la source
Je ne comprends pas pourquoi vous voulez séparer la "logique métier" de l '"objet métier". Cela ressemble à une distorsion de l'orientation de l'objet, et vous finirez par vous nouer en adoptant cette approche.
la source
Je ne pense pas qu'il existe une solution qui ne soit pas pire que le problème, tout ce qu'il a ci-dessus nécessite une usine statique publique, ce qui, à mon humble avis, est un problème pire et n'empêchera pas les gens d'appeler l'usine pour utiliser votre objet - cela ne cache rien. Il est préférable d'exposer une interface et / ou de conserver le constructeur comme interne si vous le pouvez, c'est la meilleure protection car l'assembly est du code de confiance.
Une option est d'avoir un constructeur statique qui enregistre une fabrique quelque part avec quelque chose comme un conteneur IOC.
la source
Voici une autre solution dans la veine du "juste parce que vous pouvez ne signifie pas que vous devriez" ...
Il répond aux exigences de garder le constructeur d'objet métier privé et de placer la logique d'usine dans une autre classe. Après cela, cela devient un peu sommaire.
La classe de fabrique a une méthode statique pour créer des objets métier. Il dérive de la classe d'objet métier afin d'accéder à une méthode de construction protégée statique qui appelle le constructeur privé.
La fabrique est abstraite, vous ne pouvez donc pas en créer une instance (car ce serait également un objet métier, ce serait donc bizarre), et elle a un constructeur privé afin que le code client ne puisse pas en dériver.
Ce qui n'est pas empêché, c'est que le code client dérive également de la classe d'objet métier et appelle la méthode de construction statique protégée (mais non validée). Ou pire, en appelant le constructeur par défaut protégé, nous avons dû ajouter pour obtenir la classe d'usine à compiler en premier lieu. (Ce qui, par ailleurs, est susceptible de poser problème avec tout modèle qui sépare la classe de fabrique de la classe d'objets métier.)
Je n'essaie pas de suggérer à quiconque sain d'esprit de faire quelque chose comme ça, mais c'était un exercice intéressant. FWIW, ma solution préférée serait d'utiliser un constructeur interne et la limite d'assemblage comme garde.
la source
J'apprécierais entendre quelques réflexions sur cette solution. Le seul capable de créer «MyClassPrivilegeKey» est l'usine. et 'MyClass' l'exige dans le constructeur. Évitant ainsi la réflexion sur les entrepreneurs privés / «enregistrement» à l'usine.
la source