web-dev-qa-db-fra.com

Modèle d'usine abstraite

  1. Un bon exemple pour le modèle d'usine abstrait en C #?
  2. Quels sont les avantages du modèle d'usine abstraite en C #?
  3. Comment utiliser les génériques C # avec le modèle d'usine abstrait?
  4. Comment effectuer un test unitaire avec le modèle d'usine abstrait?
47

Tout d'abord, je vous suggère de lire sur le modèle Abstract Factory, par exemple ici . Je vais maintenant essayer d'expliquer pourquoi vous utiliseriez ce modèle.

Normalement, si vous utilisez le motif Factory, vous créerez des objets dans une Factory. Le problème se pose lorsque vous avez plusieurs implémentations d'une classe (ou des classes) donnée. Maintenant, ces multiples implémentations sont regroupées. Vous utiliserez le Abstract Factory pattern lorsque vous avez une usine, mais que vous souhaitez regrouper la création d'objets par groupe.

D'accord, l'explication ci-dessus n'est peut-être pas complètement claire, je vais donc vous donner un exemple.

Disons que vous avez une bibliothèque de classes avec des agents de données. Les agents de données vous fournissent des méthodes pour accéder et stocker différentes données. Bien sûr, il existe plusieurs façons de stocker vos données. Par exemple: dans une base de données, dans un fichier XML, sur un service,. Pour chacune de ces manières possibles, vous aimeriez avoir des agents de données. Maintenant, le problème est que vous ne voulez pas que quelqu'un utilise le DataAgentA pour les fichiers XML avec DataAgentB pour la base de données (supposons que nous avons les entités A et B). L'utilisateur ne doit utiliser qu'un seul moteur de stockage.

Permettez-moi de vous présenter le modèle Abstract Factory.

Vous vous assurerez que les utilisateurs ne peuvent pas instancier directement vos agents de données, mais ils devront retirer ces agents de données d'une usine. (Un avantage supplémentaire est que lorsque vous utilisez par exemple une base de données (EF), vous pouvez effectuer un câblage interne pour vous assurer que vos agents de données utilisent le même contexte, etc.) Comment pouvons-nous accomplir cela? Nous avons défini le constructeur de nos agents de données sur "interne". En dehors de cela, nous créons des usines différentes pour chaque moteur de stockage. Maintenant, puisque ces usines font toutes la même chose, nous avons également ces interfaces (tout comme nos agents de données, car ils doivent tous faire la même chose, n'est-ce pas??).

Ci-dessous, nous avons nos interfaces. Fondamentalement, c'est le modèle d'usine, mais seulement maintenant au lieu d'environ classes, nous parlons de interfaces.

public interface IAgentA 
{
    // Add some methods here!
}

public interface IAgentB
{
    // Add some methods here!
}

public interface IAgentFactory
{
    IAgentA CreateAgentA();
    IAgentB CreateAgentB();
}

Maintenant pour les deux agents, nous avons deux implémentations possibles, une pour XML et une pour le stockage de base de données (encore une fois: ceci est un exemple, vous pouvez avoir autant de types d'implémentation que vous le souhaitez). Ces implémentations ressembleraient à ceci (voir ci-dessous). Veuillez noter que j'ai fait le constructeur internal! Cela est nécessaire pour la partie qui vient après ce bloc de code.

public class AgentA_Xml : IAgentA
{
    internal AgentA_Xml()
    { /* Construction here */}

    // IAgentA method implementations
}

public class AgentB_Xml : IAgentB
{
    internal AgentB_Xml()
    { /* Construction here */}

    // IAgentB method implementations
}


public class AgentA_Database : IAgentA
{
    internal AgentA_Database()
    { /* Construction here */}

    // IAgentA method implementations
}

public class AgentB_Database : IAgentB
{
    internal AgentB_Database()
    { /* Construction here */}

    // IAgentB method implementations
}

Maintenant que les constructeurs sont internes. Cela provoque que vous ne pouvez pas instancier ces classes en dehors de l'assembly, ce qui est généralement ce que vous faites avec ce type de cas. Nous devons maintenant créer nos usines.

public class XMLAgentFactory : IAgentFactory
{
    public IAgentA CreateAgentA()
    {
        return new AgentA_Xml();
    }

    public IAgentB CreateAgentB()
    {
        return new AgentB_Xml();
    }
}


public class DatabaseAgentFactory : IAgentFactory
{
    public IAgentA CreateAgentA()
    {
        return new AgentA_Database();
    }

    public IAgentB CreateAgentB()
    {
        return new AgentB_Database();
    }
}

Étant donné que les deux usines implémentent l'interface IAgentFactory, l'utilisateur peut facilement changer d'implémentation de AgentFactory (s'il veut, dans ce cas, utiliser un moteur de stockage différent) sans avoir à changer tout autre code qu'il écrit (contre les agents), tant qu'il a programmé contre les interfaces (évidemment).

J'espère que l'explication ci-dessus répond à vos questions (1) et (2).

  1. Un bon exemple pour le modèle d'usine abstrait en C #?
  2. et quels sont les avantages du modèle d'usine abstrait en c #?

Répondre à votre question (3).

  1. Comment utiliser les génériques C # avec le modèle d'usine abstrait?

Vous pouvez toujours utiliser des génériques, cela ne change pas du tout lorsque vous utilisez un modèle Abstract Factory. Bien sûr, vous devrez créer des méthodes d'usine génériques (les méthodes de création), mais cela ne devrait pas poser de problème.

Répondre à votre question (4).

  1. Comment le test unitaire avec le modèle d'usine abstrait?

Exactement comme vous le feriez pour les tests unitaires de n'importe quelle autre classe. Une seule chose sera différente.

Étant donné que vous souhaitez probablement tester le constructeur de vos classes (et peut-être d'autres méthodes internes), vous devez rendre les constructeurs internes (méthodes) visibles pour votre projet de test unitaire (et vous ne voulez pas modifier le internal à public). Cela se fait facilement en ajoutant la ligne suivante à votre AssemblyInfo.cs fichier de votre projet (le projet où se trouvent votre usine et vos classes):

[Assembly: System.Runtime.CompilerServices.InternalsVisibleTo("My.UnitTest.Namespace")]

Vous pouvez trouver plus d'informations (et remarques) sur l'attribut InternalsVisibleTo sur [~ # ~] msdn [~ # ~] .

J'espère que ce genre de réponses à votre question.

140
Styxxy