使用Autofac切换抽象工厂的具体实现

时间:2016-10-28 05:47:46

标签: c# design-patterns autofac factory

我对与AutoFac和抽象工厂模式相关的问题感到羞愧。我的示例是一个使用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);
    }
}

服务应该通过构造函数注入来提取Factory。

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 ...

这是解决问题的正确方法吗?我不相信自己的设计,因为它在容器初始化之前强制用户输入。如果用户必须在运行时更改存储库,该怎么办?抽象工厂模式是解决这个问题的正确工具吗?

2 个答案:

答案 0 :(得分:1)

由于在配置容器时已知存储库类型,因此应直接注册特定存储库。没有必要引入工厂,因为hardly ever is a reason引入工厂抽象。

示例:

BeerShop

此代码允许IRepository被简化,因为its constructor would be simple现在它只取决于IRepositoryFactory而不是IRepository$('.sel_item_specify').change(function(){ var getval = $(".sel_item_specify option:selected" ).val(); alert('selectbox value '+getval); if(getval == 'other'){ $(this).closest("tr").find("button").show(); $(this).closest("tr").find("input:text").show(); $(this).closest("tr").find("input:text").attr('disabled',false); $(this).attr('disabled','disabled'); } else{ $(".itemremove_btn").click(function(){ $(this).hide(); $(this).closest("tr").find("input:text").attr('disabled','disabled'); $(this).closest("tr").find("select").attr('disabled',false); }); } });。这简化了测试并使得更容易推理这个类。此外,它删除了不必要的抽象。

答案 1 :(得分: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或其他设置文件中读取全局单例,而不是全局单例。

您现在希望为每种受支持的IRepositoryBuilder类型

实施IRepository