35

我在Bootstrapper中配置Automapper,我打电话给Application_Start()中的Bootstrap(),我被告知这是错误的,因为每次我必须添加一个新映射时,我必须修改我的Bootstrapper类,所以我违反了开放原则。在Bootstrapper中配置Automapper违反开闭原则?

您怎么看,我真的违反这个原则吗?

public static class Bootstrapper 
{ 
    public static void BootStrap() 
    { 
     ModelBinders.Binders.DefaultBinder = new MyModelBinder(); 
     InputBuilder.BootStrap(); 
     ConfigureAutoMapper(); 
    } 

    public static void ConfigureAutoMapper() 
    { 
     Mapper.CreateMap<User, UserDisplay>() 
      .ForMember(o => o.UserRolesDescription, 
         opt => opt.ResolveUsing<RoleValueResolver>()); 
     Mapper.CreateMap<Organisation, OrganisationDisplay>(); 
     Mapper.CreateMap<Organisation, OrganisationOpenDisplay>(); 
     Mapper.CreateMap<OrganisationAddress, OrganisationAddressDisplay>(); 
    }  
} 

回答

39

我认为你违反了两个原则:单一责任原则(SRP)和开放/封闭原则(OCP)。

您正在违反SRP,因为引导类有多个更改原因:如果您更改模型绑定或自动映射程序配置。

如果您要添加额外的引导代码来配置系统的另一个子组件,那么您将违反OCP。

我通常如何处理这个问题是我定义了以下接口。

public interface IGlobalConfiguration 
{ 
    void Configure(); 
} 

对于需要引导的系统中的每个组件,我将创建一个实现该接口的类。

public class AutoMapperGlobalConfiguration : IGlobalConfiguration 
{ 
    private readonly IConfiguration configuration; 

    public AutoMapperGlobalConfiguration(IConfiguration configuration) 
    { 
     this.configuration = configuration; 
    } 

    public void Configure() 
    { 
     // Add AutoMapper configuration here. 
    } 
} 

public class ModelBindersGlobalConfiguration : IGlobalConfiguration 
{ 
    private readonly ModelBinderDictionary binders; 

    public ModelBindersGlobalConfiguration(ModelBinderDictionary binders) 
    { 
     this.binders = binders; 
    } 

    public void Configure() 
    { 
     // Add model binding configuration here. 
    } 
} 

我使用Ninject来注入依赖关系。IConfiguration是静态AutoMapper类的底层实现,ModelBinderDictionaryModelBinders.Binder对象。然后,我将定义一个NinjectModule,它将扫描指定程序集以获取实现IGlobalConfiguration接口的任何类,并将这些类添加到组合中。

public class GlobalConfigurationModule : NinjectModule 
{ 
    private readonly Assembly assembly; 

    public GlobalConfigurationModule() 
     : this(Assembly.GetExecutingAssembly()) { } 

    public GlobalConfigurationModule(Assembly assembly) 
    { 
     this.assembly = assembly; 
    } 

    public override void Load() 
    { 
     GlobalConfigurationComposite composite = 
      new GlobalConfigurationComposite(); 

     IEnumerable<Type> types = 
      assembly.GetExportedTypes().GetTypeOf<IGlobalConfiguration>() 
       .SkipAnyTypeOf<IComposite<IGlobalConfiguration>>(); 

     foreach (var type in types) 
     { 
      IGlobalConfiguration configuration = 
       (IGlobalConfiguration)Kernel.Get(type); 
      composite.Add(configuration); 
     } 

     Bind<IGlobalConfiguration>().ToConstant(composite); 
    } 
} 

然后,我会将以下代码添加到Global.asax文件中。

public class MvcApplication : HttpApplication 
{ 
    public void Application_Start() 
    { 
     IKernel kernel = new StandardKernel(
      new AutoMapperModule(), 
      new MvcModule(), 
      new GlobalConfigurationModule() 
     ); 

     Kernel.Get<IGlobalConfiguration>().Configure(); 
    } 
} 

现在我的引导代码符合SRP和OCP。我可以通过创建一个实现IGlobalConfiguration接口的类轻松地添加额外的引导代码,而我的全局配置类只有一个理由需要更改。

+3

,而且您仍然必须更改Configure方法在AutoMapperGlobalConfiguration中每次需要添加一个新的映射时 – Omu 2009-11-28 06:27:43

+10

但是这不会违反OCP。 OCP不写入一次再也不会触摸。 OCP指出引导代码的使用者GlobalConfigurationModule(GCM)应该依赖于抽象而不是实现。如果我要为log4net添加引导,我将创建一个类Log4NetGlobalConfiguration类来实现IGlobalConfiguration。然而,我不需要修改我的代码的任何其他部分,也不必修改GCM,因为它没有关于IGlobalConfiguration接口的conrete实现的复杂知识。 – mrydengren 2009-11-28 07:36:07

+0

我有疑问。一旦执行Mapper.CreateMap <>(),映射就会存在,直到应用程序关闭? – JPCF 2011-01-03 22:56:06

3

要使它完全关闭,您可以为每个映射注册创建一个静态初始化器,但这样做会矫枉过正。

从能够进行逆向工程的角度来看,有些东西实际上可以集中到一定程度。

在NInject中,每个项目或子系统(项目集合)都有一个Module的概念,这似乎是一个明智的折中方案。

2

如果有什么其违反的单一职责原则,因为班级有一个以上的理由改变。

我个人会有一个ConfigureAutoMapper类,这是我用AutoMapper完成的所有配置。但可以认为这归结于个人选择。

+0

是的,而且在我将它移动到另一个类后,我不会脱离开放闭合原理 – Omu 2009-11-27 14:50:52

2

Omu,当涉及到在我的应用程序启动例程中引导IoC容器时,我遇到了类似的问题。对于IoC,我所给出的指导指出了集中配置的优势,而不是在添加更改时将其散布在您的应用中。为了配置AutoMapper,我认为集中化的优势不那么重要。如果您可以将AutoMapper容器放入IoC容器或服务定位器中,我同意Ruben Bartelink的建议,即为每个程序集或静态构造函数或分散的构件配置一次映射。

基本上,我把它看作是决定你是要集中自举还是分散它。如果您对启动例程中的开放/关闭原则感到担忧,请将其分散。但是您可以拨打OCP的支持来换取您在一个地方完成的所有引导程序的价值。假设AutoMapper有这样一个概念,另一个选择是让引导程序扫描注册表的某些程序集。

3

我知道这是一个旧的,但您可能有兴趣知道我已经创建了一个名为Bootstrapper的开源库,可以处理这个问题。你可能想看看它。 为了避免违反OC原则,您需要在实现IMapCreater的单独类中定义映射器。 Boostrapper将使用反射来查找这些类,并将在启动时初始化所有映射器

相关问题