Подтвердить что ты не робот

Конфигурирование Automapper в Bootstrapper нарушает принцип Open-Closed?

Я настраиваю Automapper в Bootstrapper, и я вызываю Bootstrap() в Application_Start(), и мне сказали, что это неправильно, потому что я должен изменить класс Bootstrapper каждый раз, когда мне нужно добавить новое отображение, поэтому я нарушаю принцип открытого закрытия.

Как вы думаете, действительно ли я нарушаю этот принцип?

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>();
    }    
}
4b9b3361

Ответ 1

Я бы сказал, что вы нарушаете два принципа: принцип единой ответственности (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, а ModelBinderDictionary - объект ModelBinders.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, и мои глобальные классы конфигурации имеют только одну причину для изменения.

Ответ 2

Чтобы он был полностью закрыт, вы могли бы иметь статический инициализатор для регистрации картографирования, но это было бы излишним.

Некоторые вещи действительно полезны для централизации до степени с точки зрения возможности реверсирования.

В NInject существует понятие наличия Module для каждого проекта или подсистемы (набора проектов), что кажется разумным компромиссом.

Ответ 3

Я знаю, что это старый, но вам может быть интересно узнать, что я создал библиотеку с открытым исходным кодом под названием Bootstrapper, которая касается именно этой проблемы. Вы можете проверить это. Чтобы не нарушать принцип OC, вам необходимо определить ваши картографы в отдельных классах, которые реализуют IMapCreater. Boostrapper найдет эти классы с помощью отражения и инициализирует все картографы при запуске

Ответ 4

Если что-то сводится к принципу единственной ответственности, который вы нарушаете, в том, что у класса есть несколько причин для изменения.

У меня лично был бы класс ConfigureAutoMapper, с которым была выполнена вся моя конфигурация для AutoMapper. Но можно утверждать, что это зависит от личного выбора.

Ответ 5

Ому, я борюсь с подобными вопросами, когда дело доходит до начальной загрузки контейнера IoC в моей программе запуска приложения. Для IoC руководство, которое мне дано, указывает на то, что вы можете централизовать свою конфигурацию, а не разбрызгивать ее по всему приложению при добавлении изменений. Для настройки AutoMapper я считаю, что преимущество централизации гораздо менее важно. Если вы можете получить свой контейнер AutoMapper в свой контейнер IoC или Service Locator, я согласен с предложением Ruben Bartelink о настройке сопоставлений один раз на сборку или в статических конструкторах или что-то децентрализованное.

В принципе, я рассматриваю это как вопрос о том, хотите ли вы централизовать загрузку или децентрализовать ее. Если вы обеспокоены принципом Open/Closed в вашей рутине запуска, пойдите с децентрализацией. Но ваша привязка к OCP может быть набрана в обмен на ценность всей вашей начальной загрузки, сделанной в одном месте. Другим вариантом было бы, чтобы bootstrapper просматривал определенные сборки для реестров, предполагая, что AutoMapper имеет такую ​​концепцию.