2017-10-16 4 views
1

J'ai un projet Asp.net Core avec une DI par défaut. Donc j'obtiens des instances de services BL, des référentiels, des contextes EF par DI. J'ai une méthode abstraite qui renvoie un certain type par paramètre.Comment refactoriser la méthode abstraite à DI

IDocumentPreprocessor CreateDocumentPreprocessor(DocType docType) 
    { 
     switch (docType) 
     { 
      case DocType.Txt: 
       return new TxtPreprocessor(_context, _docRepository); 
      case DocType.Doc: 
       return new DocPreprocessor(_docRepository); 

      default: 
       throw new ... 
     } 
    } 

Je n'aime pas ici la création directe d'instance par "new". Mais je ne suis pas sûr s'il est possible de passer cette logique à DI. Donc la question - comment le refactoriser à l'utilisation DI?

Répondre

3

Vous encapsulez la logique dans un autre service injectable DI: un IDocumentPreprocessorFactory. Là vous injectez des méthodes d'usine pour les implémentations IDocumentPreprocessor.

public interface IDocumentPreprocessorFactory 
{ 
    IDocumentPreprocessor CreateDocumentPreprocessor(DocType docType); 
} 

public class DocumentPreprocessorFactory : IDocumentPreprocessorFactory 
{ 
    private readonly Func<TxtPreprocessor> createTxtPreprocessor; 

    private readonly Func<DocPreprocessor> createDocPreprocessor; 

    public DocumentPreprocessorFactory(
     Func<TxtPreprocessor> createTxtPreprocessor, 
     Func<DocPreprocessor> createDocPreprocessor) 
    { 
     this.createTxtPreprocessor = createTxtPreprocessor; 
     this.createDocPreprocessor = createDocPreprocessor; 
    } 

    public IDocumentPreprocessor CreateDocumentPreprocessor(DocType docType) 
    { 
     switch (docType) 
     { 
      case DocType.Txt: 
       return this.createTxtPreprocessor(); 
      case DocType.Doc: 
       return this.createDocPreprocessor(); 
      default: 
       throw new... 
     } 
    } 
} 

Vous devez maintenant étendre votre configuration DI avec les enregistrements des méthodes d'usine. Je ne l'ai pas encore utilisé Core DI, mais je crois qu'il pourrait ressembler à ceci

services.AddSingleton<Func<DocPreprocessor>>(ctx =>() => ctx.GetService<DocPreprocessor()); 
services.AddSingleton<Func<TxtPreprocessor>>(ctx =>() => ctx.GetService<TxtPreprocessor()); 
+1

À un certain point, si OP souhaite un nouveau 'DocType', tel que« DocType.Pdf », une modification ne sera-t-elle pas requise dans la classe 'DocumentPreprocessorFactory'? Bien qu'il soit relativement simple d'ajouter le nouveau type, ne viole-t-il pas le principe Open Closed pour modifier la classe 'DocumentPreprocessorFactory' afin d'inclure la nouvelle logique d'ajout de la fonctionnalité Pdf? Pareil si OP veut finalement 'DocType.Xml', etc? –

+0

@ mike-maurer, vous avez absolument raison. Mais refactoriser le bloc 'switch' de l'OP pour qu'il soit ouvert-fermé est une toute autre histoire. –

+0

intéressant. Je serais curieux de voir s'il y a une implémentation qui résout cela sans violer les principes SOLID. –

1

cela peut être vous aider ..

 // object lifetime transient or others.. determine according to your needs 
     services.AddTransient<TxtPreprocessor>(); 
     services.AddTransient<DocPreprocessor>(); 
     services.AddTransient(processorFactory => 
     { 
      Func<DocType, IDocumentPreprocessor> factoryFunc = docType => 
      { 
       switch (docType) 
       { 
        case DocType.Txt: 
         return processorFactory.GetService<TxtPreprocessor>(); 
        default: 
         return processorFactory.GetService<DocPreprocessor>();// DocPreprocessor is defult 
       } 
      }; 
      return factoryFunc; 
     }); 
utilisation

dans une classe enregistrée ..

public class AnyClass 
{ 
    private readonly IDocumentPreprocessor _documentProcessor; 

    public AnyClass(Func<DocType, IDocumentPreprocessor> factoryFunc) 
    { 
     _documentProcessor = factoryFunc(DocType.Doc); 
    } 
}