89 votes

Modèle d'usine en C #: Comment s'assurer qu'une instance d'objet ne peut être créée que par une classe d'usine?

Récemment, j'ai eu la réflexion sur la sécurisation de certains de mon code. Je suis curieux de voir comment on pourrait faire en sorte qu'un objet ne peut jamais être créés directement, mais uniquement par le biais d'une méthode d'une classe factory. Disons que j'ai quelques "business object" de la classe et je veux m'assurer que toute instance de cette classe sera valide, l'état interne. Afin de réaliser ce que j'ai besoin pour effectuer certaines vérifier avant de créer un objet, probablement dans son constructeur. C'est tout bon jusqu'à ce que je décide de faire cette vérification, une partie de la logique métier. Alors, comment puis-je organiser un objet métier pour être réalisable uniquement grâce à une méthode dans ma classe de logique métier, mais jamais directement? Le premier désir naturel d'utiliser un bon vieux "ami" mot clé de C++ en deçà avec C#. Nous avons donc besoin d'autres options...

Essayons quelques exemple:

public MyBusinessObjectClass
{
    public string MyProperty { get; private set; }

    public MyBoClass (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 est ok jusqu'à ce que vous vous rappelez que vous pouvez toujours créer MyBusinessObjectClass instance directement, sans contrôle de l'entrée. Je voudrais exclure cette possibilité technique tout à fait.

Donc, ce que pense la communauté de cela?

62voto

Jon Skeet Points 692016

Vous pouvez rendre le constructeur privé et l’usine un type imbriqué:

 public class BusinessObject
{
    private BusinessObject(string property)
    {
    }

    public class Factory
    {
        public static BusinessObject CreateBusinessObject(string property)
        {
            return new BusinessObject(property);
        }
    }
}
 

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 ça va aider ...

56voto

Ricardo Nolde Points 5630

On dirait que vous voulez juste exécuter une logique métier avant de créer l'objet - alors pourquoi ne pas simplement créer une méthode statique à l'intérieur de "BusinessClass" qui effectue tout le sale travail de vérification "myProperty" et rend le constructeur privé?

 public BusinessClass
{
    public string MyProperty { get; private set; }

    private BusinessClass()
    {
    }

    private BusinessClass(string myProperty)
    {
        MyProperty = myProperty;
    }

    public static BusinessClass CreateObject(string myProperty)
    {
        // Perform some check on myProperty

        if (/* all ok */)
            return new BusinessClass(myProperty);

        return null;
    }
}
 

Il serait assez simple de l'appeler:

 BusinessClass objBusiness = BusinessClass.CreateObject(someProperty);
 

51voto

Fabian Schmied Points 1073

Ou, si vous voulez vraiment vous en passer, inversez le contrôle: demandez à la classe de renvoyer l’usine et instrumentez-la avec un délégué pouvant créer la classe.

 public class BusinessObject
{
  public static BusinessObjectFactory GetFactory()
  {
    return new BusinessObjectFactory (p => return new BusinessObject (p));
  }

  private BusinessObject(string property)
  {
  }
}

public class BusinessObjectFactory
{
  private Func<string, BusinessObject> _ctorCaller;

  public BusinessObjectFactory (Func<string, BusinessObject> ctorCaller)
  {
    _ctorCaller = ctorCaller;
  }

  public BusinessObject CreateBusinessObject(string myProperty)
  {
    if (...)
      return _ctorCaller (myProperty);
    else
      return null;
  }
}
 

:)

15voto

Matt Hamilton Points 98268

Vous pouvez créer le constructeur sur votre classe interne MyBusinessObjectClass et le déplacer, ainsi que l’usine dans leur propre assemblage. Maintenant, seule l’usine devrait pouvoir construire une instance de la classe.

7voto

Fabian Schmied Points 1073

Indépendamment de ce que Jon a suggéré, vous pouvez également soit faire en sorte que la méthode usine (y compris le contrôle) soit une méthode statique de BusinessObject. Ensuite, laissez le constructeur privé, et tous les autres seront obligés d'utiliser la méthode statique.

 public class BusinessObject
{
  public static Create (string myProperty)
  {
    if (...)
      return new BusinessObject (myProperty);
    else
      return null;
  }
}
 

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?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X