2010-03-09 5 views
2

Je pensais lancer un nouveau projet en C# .NET (4.0RC) depuis quelques jours maintenant. Le résultat final du projet serait une bibliothèque de classes composée de ce que j'appelle pour l'instant une "ProviderFactory", un couple d'interfaces (ou de classes abstraites) et un "ProviderType" par défaut ou deux. Tout d'abord, je pourrais avoir besoin/décider de changer le nom de mes classes pour ne pas entrer en conflit avec ce qui est déjà dans .NET. Deuxièmement, il y a de bonnes chances que ce que je décris ici existe déjà (il pourrait même être dans .NET nativement sans que je le sache), donc si c'est le cas, un seul lien vers ce projet ou un tutoriel serait apprécié.Ideatryout: Typed Providerfactory

La fonctionnalité à laquelle je pense n'est pas seulement une usine qui peut me fournir un simple fournisseur, mais elle devrait être capable de générer n'importe quel type de fournisseur. Je veux être capable d'écrire quelque chose comme ceci:

BlogProvider bp = ProviderFactory.GetProvider<BlogProvider>(); 

et il doit retourner le BlogProvider courant sélectionné ou null s'il n'y en a pas. Aussi je veux être en mesure de fournir une liste sur tous les fournisseurs disponibles d'un type comme par exemple

string[] blogproviders = ProviderFactory.GetRegisteredProviders<BlogProvider>(); 

et il devrait retourner un tableau de chaînes avec blogs fournisseurs (comme « ODBCBlogProvider », « XMLBlogProvider », etc.). Je dois aussi être en mesure de définir le fournisseur actuel, par exemple comme ceci:

ProviderFactory.SetCurrentProvider<BlogProvider>("ODBCBlogProvider"); 

Cela devrait rendre le courant BlogProvider la ODBCBlogProvider, de sorte que la prochaine fois que j'appelle ProviderFactory.GetProvider() il doit retourner une instance de ODBCBlogProvider (qui de cause doit étendre la classe de base BlogProvider). En outre, chaque ProviderType (ou ProviderBase si vous le souhaitez) doit avoir un ProviderTypeName (BlogProvider) et un ProviderTypeDisplayName ("Blog" ou "Blog fournisseur") propre à ce ProviderType, et chaque Fournisseur qui étend ce fournisseur doit avoir un ProviderName (ODBCBlogProvider) et un ProviderDisplayName ("ODBC" ou "blog ODBC" etc.). Le ProviderName et le ProviderTypeName pourraient simplement être des noms de classes acquis par réflexion.

Cela signifie probablement que je besoin d'une interface pour le ProviderBase, quelque chose comme ceci:

public interface ProviderBase 
{ 
    string ProviderName { get; } // The display-name. 
    string Description { get; } // A description of the provider type, like "This provides you with a blog." 
} 

Et pour BlogProvider je besoin de créer une classe abstraite qui implémente ces deux membres et crée des méthodes pour les BlogProviders réels à remplacer.

public abstract class BlogProvider : ProviderBase 
{ 
    public string ProviderName 
    { 
     get { return "Blog"; } 
    } 

    public string Description 
    { 
     get { return "This provides you with a blog."; } 
    } 

    public abstract int GetPostCount(); 
} 

Puis, quelque part dans mon code, j'avais dû exécuter quelque chose comme

ProviderFactory.RegisterProviderType(typeof(BlogProvider)); 

ou une option serait encore mieux de pouvoir utiliser des attributs comme ceci:

[Provider("Blog", "This provides you with a blog.")] 
public interface BlogProvider 
{ 
    int GetPostCount(); 
} 

Je ne vois pas la nécessité d'utiliser une classe abstraite avec cette approche car aucun des membres n'a besoin d'être défini dans la classe de base. Le besoin de l'interface ProviderBase a également disparu. L'idéal serait de savoir si ProviderFactory pouvait simplement trouver toutes les classes/interfaces contenant ProviderAttribute, et que ODBCBlogProvider aurait juste besoin d'implémenter BlogProvider, mais je ne suis pas certain de comment je fais cela, surtout pas quand c'est une exigence les fournisseurs et providertypes peuvent tous deux venir pour des assemblées externes.Une autre exigence de ProviderFactory est qu'il doit fonctionner à la fois dans un environnement sans état et dans un environnement où il est maintenu actif. Il doit donc pouvoir enregistrer les paramètres dans un fichier config-file/stream/database (quelque chose comme cela, n'a pas vraiment d'importance) sur le changement et charger à partir de ce à l'initialisation, mais cela devrait être facultatif. Ainsi, il fonctionnerait également sur le web (avoir un blog sur votre propre machine ne donne pas trop de sens).

Une exigence de plus est que les fournisseurs peuvent besoin d'autres fournisseurs de travailler. Par exemple, AudiEngine peut uniquement fonctionner avec une AudiCar, mais une AudiCar peut fonctionner parfaitement avec un BmwEngine.

aussi (et je ne suis pas sûr où mettre encore là, peut-être un attribut facultatif sur le fournisseur de classe elle-même) chaque fournisseur doit avoir la possibilité d'être singleton ou non.

C'est tout ce que je peux penser pour l'instant. Toute contribution sur la façon dont cela devrait être mis en œuvre ou la météo là est déjà une mise en œuvre là-bas serait grandement appréciée, et tout peut être changé parce que je n'ai pas encore écrit de code, c'est quelque chose que je pensais pour l'instant.

Répondre

2

Eh bien, il ressemble à un overengineered (plutôt trop-fantaisie nommé) contenant IoC/DI.

L'idée de IoC est à peu près la même chose, à l'exception qu'ils (conteneurs) ne nécessitent pas vos « collaborateurs » pour mettre en œuvre des interfaces ou dériver des classes de base:

var builder = new ContainerBuilder(); 
builder.Register<Car>(); 
builder.Register<Engine>().As<IEngine>(); 

var container = builder.Build(); 

// somewhere in the code 
var car = container.Resolve<Car>(); 

Voici un exemple en utilisant autofac, qui est l'un d'une myriade de conteneurs DI pour C# /. NET.

+0

Mes pensées exactement –

+0

Par « collaborateurs » vous dire que je les ai donné à chacun des noms et des choses? Eh bien, certains des points avec ce système est cette même collaboration. L'idée est que certains utilisateur final devrait être en mesure d'entrer dans une interface utilisateur (Web ou gagner, peu importe) et choisir que de ce système auto-fournisseurs disponibles (volvo, audi, saab), la voiture fournisseur audi devrait être utilisé, mais le moteur devrait être BMW. – Alxandr

0

Comme @Anton indique, c'est à proximité d'un conteneur IoC/DI. Encore une fois, en utilisant Autofac, vous pouvez obtenir un long chemin à faire quelque chose comme ceci:

var builder = new ContainerBuilder(); 
builder.Register<Car>(); 
builder.Register<BmwEngine>(); 
builder.Register<AudiEngine>(); 
builder.Register<DefaultEngine>(); 
builder.Register<EngineSelector>().As<IEngineSelector>().SingletonScope(); 

builder.Register(
    c => { 
      var selector = c.Resolve<IEngineSelector>(); 
      switch(selector.CurrentEngine) 
      { 
       case "bmw": 
        return c.Resolve<BmwEngine>(); 
       case "audi": 
        return c.Resolve<AudiEngine>(); 
       default: 
        return c.Resolve<DefaultEngine>(); 
      } 
     } 
    ).As<IEngine>().FactoryScoped(); 

var container = builder.Build(); 

// somewhere in the code 
var selector = container.Resolve<IEngineSelector>(); 
selector.CurrentEngine = "bmw"; 

var car = container.Resolve<Car>(); 
+0

Désolé, mais je pense que je vais finir avec le code vraiment malpropre et laid si j'essaie de faire ce que je veux utiliser quelque chose comme autofac. Peut-être que si je l'ai utilisé dans le dos de certains autres cours, mais pas encore sûr. En outre, une exigence que j'ai oublié était la capacité pour un «fournisseur» d'exiger d'autres «fournisseurs». En d'autres termes, l'AudiEngine pourrait ne fonctionner que dans AudiCars, mais AudiCars n'a pas de problème avec BmwEngines. – Alxandr

+0

Oui, vous devriez absolument envelopper la plus grande partie de la complexité dans des classes/méthodes séparées. Votre deuxième exigence est certainement possible, nous cherchons à fournir des métadonnées sur les «fournisseurs». Autofac 2 est beaucoup plus fort dans ce domaine et pourrait être quelque chose à regarder. –