2016-10-28 5 views
2

私はAutoFacと抽象的なファクトリパターンに関する問題を抱えています。 My Exampleは、IRepositoryFactoryを使用して、ユーザー入力に関連するJSONまたはInMemoryに基づいてリポジトリを作成するサービスです。オートファックを使用して抽象ファクトリの具体的な実装を切り替える

// Abstract Factory 
public interface IRepositoryFactory{ 
    IRepository Create(string databaseIdentifier); 
} 

// JSON 
public class JsonRepositoryFactory{ 
    public IRepository Create(string databaseIdentifier){ 
     return new JsonRepository(databaseIdentifier); 
    } 
} 

// InMemory 
public class MemoryRepository{ 
    public IRepository Create(string databaseIdentifier){ 
     return new MemoryRepository(databaseIdentifier); 
    } 
} 

サービスはコンストラクタインジェクションによってファクトリをプルする必要があります。

public interface IShopService{ 
    public string Name {get;} 
} 

public class BeerShop : IShopService { 
    public string Name {get; private set;} 
    private readonly IRepository _repository; 

    public BeerShop(IRepositoryFactory repositoryFactory){ 
     Name = "beershop"; 
     _repository = repositoryFactory.Create(Name); 
    } 
} 

これまではこれでうまくいきます。しかし初期設定は私の好みではありません。

var builder = new ContainerBuilder(); 
var userInput = ReadInput(); 

if(userInput = "json") 
    builder.RegisterType<IRepositoryFactory>().As<JsonRepositoryFactory>(); 
else 
    builder.RegisterType<IRepositoryFactory>().As<MemoryRepositoryFactory>(); 

builder.RegisterType<IShopService>.As<BeerShop>(); 

var container = builder.build(); 

[...]  

var service = container.Resolve<IShoptService>(); 
// and so on ... 

これは正しい解決方法ですか?コンテナの初期化前にユーザー入力を強制するため、自分の設計ではわかりません。実行時にユーザーがリポジトリを変更する必要がある場合はどうなりますか?抽象的な工場パターンは、この問題を解決するための適切なツールですか?

+0

あなたの 'BeerShop'は多すぎます:[注入コンストラクタはシンプルでなければなりません](http://blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple/)。コンストラクタから 'factory.Create'への呼び出しをComposition Rootに移すと、このファクトリ抽象化はもう必要ありません。また、[工場の抽象化はコードの臭いです](https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=100)にも注意してください。 – Steven

+0

私はStevenに同意します。あなたのクライアント(BeerShop)は、それが他のもののために同じコード/ロジックを使用するようには見えません:例:colashoop。それ以外の機会がない場合は、工場を使わないでください。工場は複雑さをもたらします。 下位レベルでこの分離が必要なようです。リポジトリは同じですが、jsonやメモリをDbとして使用します。実行時にdbを変更することは非常に危険です。あなたはjsonに何かを書いて、同じランタイムでユーザ入力によってメモリから読み込むでしょうか? –

答えて

0

実行時にファクトリの動作を変更する場合は、作成するIRepositoryのタイプを決定するロジックをファクトリ自体に移す必要があります。パターンを調整し、特定の方法に準拠させるのではなく、要件に合わせて調整することができます。

これを行う方法は次のとおりです。考えてみると、さまざまな方法で調整することができます。

public interface IRepository 
    { 
     //repository contracts 
    } 

    public interface IRepositoryFactory 
    { 
     IRepository Create(string arguments); 
    } 

    public interface IRepositoryBuilder 
    { 
     RepositoryType Type { get; } 
     IRepository Create(string args); 
    } 

    public class ApplicationSettings 
    { 
     public RepositoryType RepositoryType { get; set; } 
    } 

    public enum RepositoryType { Json, Text } 

    // Default implementation of repository factory based on applicationsettings. 
    public class ConfigurableRepositoryBuilder:IRepositoryFactory 
    { 
     private readonly ApplicationSettings _settings; 
     private readonly IEnumerable<IRepositoryBuilder> _repositoryBuilders; 

     public ConfigurableRepositoryBuilder(ApplicationSettings settings, IEnumerable<IRepositoryBuilder> repositoryBuilders) 
     { 
      _settings = settings; 
      _repositoryBuilders = repositoryBuilders; 
     } 

     public IRepository Create(string arguments) 
     { 
      var builder = _repositoryBuilders.First(x => x.Type == _settings.RepositoryType); 
      //configure builder settings and then call create 
      return builder.Create(arguments); 
     } 
    } 

これで、いつでもグローバル設定を変更することができ、次の呼び出し以降に新しいタイプのリポジトリを取得できます。グローバルシングルトンの代わりに、app.configやその他の設定ファイルからも読み込むことができます。

これで、リポジトリの種類は、コンテナが設定されている時に知られているので、あなたが直接特定のリポジトリを登録する必要がサポートIRepositoryタイプ

1

の種類ごとにIRepositoryBuilderを実装します。工場を抽象化するために、hardly ever is a reasonがあるので、工場を導入する必要はありません。

例:それは今では唯一IRepository代わりにIRepositoryFactoryIRepositoryに依存its constructor would be simpleためと

var builder = new ContainerBuilder(); 
var userInput = ReadInput(); 

if(userInput = "json") 
    builder.Register<IRepository>(c => new JsonRepository("dbidentifier")); 
else 
    builder.Register<IRepository>(c => new MemoryRepository()); 

builder.RegisterType<BeerShop>.As<IShopService>(); 

var container = builder.build(); 

[...]  

var service = container.Resolve<IShopService>(); 

このコードは、BeerShopを簡略化することが可能となります。これにより、テストが簡単になり、このクラスについてより簡単に推論することができます。さらに、不要な抽象を取り除きます。

関連する問題