35

부트 스트 래퍼에서 Automapper를 구성하고 있는데 Application_Start()Bootstrap()을 호출합니다. 새 매핑을 추가해야 할 때마다 내 Bootstrapper 클래스를 수정해야하기 때문에 잘못되었습니다. 따라서 위반하고 있습니다. 개방 원칙.부트 스트 래퍼에서 오토 매퍼 (Automapper)를 구성하면 개방 원칙 (Open-Closed Principle)을 위반합니까?

당신은 어떻게이 원리를 정말로 위반합니까?

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)의 2 개의 원리를 위반하고 있다는 것을 주장 할 것입니다.

부트 스트래핑 클래스가 모델 바인딩 또는 자동 매퍼 구성을 변경할 경우 변경해야 할 이유가 하나 이상 있으므로 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를 사용합니다.IConfigurationAutoMapper 클래스의 기본 구현이며 ModelBinderDictionaryModelBinders.Binder 개체입니다. 그 다음 IGlobalConfiguration 인터페이스를 구현하는 클래스에 대해 지정된 어셈블리를 검사하여 해당 클래스를 복합체에 추가하는 NinjectModule을 정의합니다.

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

에서 벗어나지 않고 구성을 변경해야합니다. 메서드를 AutoMapperGlobalConfiguration에 추가 할 때마다 – Omu

+10

새 매핑을 추가해야하지만 OCP를 위반하지는 않습니다. OCP는 한번도 다시 만지지 마십시오. OCP는 부트 스트래핑 코드의 소비자 인 GlobalConfigurationModule (GCM)은 구현을 구체화하지 않고 추상화에 의존해야한다고 주장합니다. log4net에 부트 스트래핑을 추가한다면 IGlobalConfiguration을 구현하는 클래스 Log4NetGlobalConfiguration 클래스를 만들 것입니다. 그러나 IGlobalConfiguration 인터페이스의 구체 구현에 대한 복잡한 지식이 없기 때문에 코드의 다른 부분은 수정할 필요가 없으며 GCM은 수정할 필요가 없습니다. – mrydengren

+0

나는 의심 스럽다. Mapper.CreateMap <>()을 실행하면 응용 프로그램이 종료 될 때까지지도가 존재합니까? – JPCF

3

완전히 닫으려면 매핑 등록 당 정적 초기화 프로그램이있을 수 있지만 과장 될 수 있습니다.

리버스 엔지니어링을 할 수 있다는 점에서 어느 정도 집중화하는 것이 실제로 유용합니다.

NInject에는 프로젝트 또는 서브 시스템 (프로젝트 집합) 당 Module이라는 개념이 있습니다. 이는 합리적인 절충안으로 보입니다.

2

위반하는 단일 책임 원칙이 있다면 그 클래스에는 변경해야 할 하나 이상의 이유가 있습니다.

개인적으로 AutoMapper에 대한 모든 구성을 완료 한 ConfigureAutoMapper 클래스가 있습니다. 그러나 그것은 개인적인 선택에 달려 있다고 주장 될 수 있습니다.

+0

예. 다른 클래스로 이동 한 후에도 닫힌 원칙 – Omu

2

Omu 나는 응용 프로그램의 시작 루틴에서 IoC 컨테이너를 부트 스트랩 할 때 비슷한 질문을 던집니다. IoC에 대한 지침은 변경 사항을 추가 할 때 앱 전체에 뿌리기보다는 구성을 중앙 집중화하는 이점을 지적합니다. AutoMapper를 구성하기 위해서는 중앙 집중화의 이점이 훨씬 덜 중요하다고 생각합니다. AutoMapper 컨테이너를 IoC 컨테이너 또는 Service Locator로 가져올 수 있다면 Ruben Bartelink가 어셈블리마다 또는 정적 생성자 또는 분산 된 것으로 매핑을 구성하는 제안에 동의합니다.

기본적으로 부트 스트랩을 중앙 집중화할지 또는 분산화할지 결정해야합니다. 시동 루틴에 대한 Open/Closed Principle에 관심이있는 분은 분권화를 진행하십시오. 그러나 OCP에 대한 준수는 한 곳에서 수행 한 모든 부트 스트래핑의 가치에 대한 대가로 전화를 걸 수 있습니다. 또 다른 옵션은 AutoMapper가 그러한 개념을 가지고 있다고 가정 할 때 부트 스트 래퍼가 레지스트리에 대해 특정 어셈블리를 검색하도록하는 것입니다.

3

나는 이것이 오래된 것임을 알고 있지만,이 문제를 정확히 다루는 Bootstrapper이라는 오픈 소스 라이브러리를 만들었습니다. 그것을 확인하고 싶을 수도 있습니다. OC 원칙을 위반하지 않으려면 IMapCreater를 구현하는 별도의 클래스에 매퍼를 정의해야합니다. Boostrapper는 리플렉션을 사용하여 이러한 클래스를 찾고 시작시 모든 매퍼를 초기화합니다.

관련 문제