35

Bootstrapper で Automapper を構成していて、 で を呼び出してBootstrap()い ますが、新しいマッピングを追加する必要があるたびにクラスApplication_Start()を変更する必要があるため、これは間違っていると言われたので、Open-Closed Principle に違反しています。 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>();
    }    
}
4

5 に答える 5

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 を使用して依存関係を注入します。は静的クラスIConfigurationの基になる実装であり、オブジェクトです。次に、指定されたアセンブリをスキャンしてインターフェイスを実装するクラスを探し、それらのクラスをコンポジットに追加する を定義します。AutoMapperModelBinderDictionaryModelBinders.BinderNinjectModuleIGlobalConfiguration

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ます。グローバル構成クラスを変更する理由は 1 つだけです。

于 2009-11-27T21:46:24.377 に答える
3

これが古いものであることは承知していますが、この問題を正確に扱うBootstrapperというオープン ソース ライブラリを私が作成したことを知りたいと思われるかもしれません。あなたはそれをチェックしたいかもしれません。OC の原則を破らないようにするには、IMapCreater を実装する別のクラスでマッパーを定義する必要があります。Boostrapper はリフレクションを使用してこれらのクラスを見つけ、起動時にすべてのマッパーを初期化します

于 2011-11-25T17:23:46.373 に答える
3

完全に閉じるには、マッピング登録ごとに静的イニシャライザーを使用できますが、それはやり過ぎです。

ただし、リバースエンジニアリングができるという観点から、ある程度集中化することで実際に役立つものもあります。

NInject には、Moduleプロジェクトまたはサブシステム (プロジェクトのセット) ごとに 1 つの概念があるため、これは賢明な妥協案のようです。

于 2009-11-27T08:35:26.167 に答える
2

どちらかといえば、クラスに変更する理由が複数あるという点で、違反しているのは単一責任の原則です。

私は個人的に、AutoMapperのすべての構成が行われたConfigureAutoMapperクラスを持っています。しかし、それは個人的な選択にかかっていると主張することができます。

于 2009-11-27T14:39:18.320 に答える
2

Omu、アプリのスタートアップルーチンでIoCコンテナをブートストラップすることに関しては、同様の質問に取り組んでいます。IoCの場合、私が提供したガイダンスは、変更を追加するときに構成をアプリ全体に散在させるのではなく、構成を一元化することの利点を示しています。AutoMapperを構成する場合、集中化の利点はそれほど重要ではないと思います。AutoMapperコンテナーをIoCコンテナーまたはServiceLocatorに取り込むことができる場合は、アセンブリごとに1回、静的コンストラクターなどでマッピングを構成するというRubenBartelinkの提案に同意します。

基本的に、ブートストラップを集中化するか分散化するかを決定する問題だと思います。スタートアップルーチンのオープン/クローズド原則について懸念がある場合は、それを分散化してください。ただし、OCPの順守は、1か所で行われるすべてのブートストラップの価値と引き換えにダイヤルダウンすることができます。AutoMapperにそのような概念があると仮定すると、別のオプションは、ブートストラッパーにレジストリの特定のアセンブリをスキャンさせることです。

于 2009-11-27T15:05:28.050 に答える