web-dev-qa-db-ja.com

DI / IoC Abstract Factory Galore

次のようなプロジェクトアーキテクチャがある場合、各ボックスはアセンブリ(すべてクラスライブラリ)を表し、各矢印は依存関係を表します。

architecture

このプロジェクトがmassive(まあ、そうなる!)であることは何の価値もないかもしれませんが、そのような分離によって生じる複雑さは絶対に正当化されます。

ビジネスロジックレイヤー

BLLライブラリは、Abstract名前空間にPOCO interfacesのさまざまなセットを定義および編成し、Concrete名前空間にさまざまな機能logicを実装します。機能ロジックは、抽象POCOインターフェースを参照して使用します。

データアクセスレイヤー

DALライブラリは、LinqをSQL(dbml)クラスに「拡張」するpartialクラスを使用してBLLからPOCOインターフェイスを実装するため、Customer L2Sクラスがある場合は、BLL ICustomerインターフェイスを実装します。 L2Sクラスに異なるタイプがある場合や、BLL抽象化がenumタイプを使用する場合がありますが、ほとんどの場合、部分クラスコードは次のようになります。

public partial class SomeEntity : ISomeEntity { }

このアセンブリは、repositoriesでコンストラクター注入された「xxxxModel」という名前のクラスを公開することにより、Modelを提供しますservicesにありますが、ひねりがあります。「xxxxxModel」クラスは、データベーストランザクションをカプセル化するIUnitOfWorkと呼ばれるインターフェースを実装します必須動作可能すべてのリポジトリで自由に使用できます...おそらく動的な(実行時に決定される)接続文字列を使用します。これを実現するために、適切なタイプの単一のDataContextを作成する「モデルファクトリ」を作成し、各リポジトリに同じインスタンスを挿入しました。

プレゼンテーションロジックレイヤー

一部のBLL POCOインターフェースには「ViewModel」実装が必要です(たとえば、リスト内の項目として表示するため)-PLLライブラリがそれらを定義します。ただし、このライブラリの目標は、実際のプレゼンテーションロジックに重点を置いていますWPFへの参照はありませんWindowBaseへの単一参照以外)しかし、私はそれを取得します。したがって、このアセンブリはViewModelを提供します。

ここで注意すべきことは、ViewModelはここで完全にcontrolsです。各ViewModelには、IViewFactoryの実装を作成するIViewの実装が注入されます。実装の問題なので、MVVMにどれほど深く違反しているのかはわかりません(正直に言うと、この点については特に気にしません-特に、このような ブログ投稿を読んだ後は )、指摘すべきことcreatesビューを文字どおりに表示し、DataContextを割り当てるのはViewModelです。これにより、shouldのように、ウィンドウを表示したり 閉じたり のように単純であることに対処することが非常に簡単になります。

したがって、すべてのViewModelクラスはViewModelBaseを継承します。これは、保護されたコンストラクターでIViewFactoryを取り、効果的にViewをインスタンス化する抽象クラスです。問題のあるコードは次のとおりです。

/// <summary>
/// An interface for a ViewModel.
/// </summary>
public interface IViewModel : INotifyPropertyChanged
{
    /// <summary>
    /// Notifies listener that the value of the specified property has changed.
    /// </summary>
    void NotifyPropertyChanged<TProperty>(Expression<Func<TProperty>> property);

    /// <summary>
    /// Notifies listener that the value of the specified property has changed.
    /// </summary>
    void NotifyPropertyChanged(string propertyName);

    /// <summary>
    /// Closes the view.
    /// </summary>
    void Close();

    /// <summary>
    /// Displays the view.
    /// </summary>
    void Show();

    /// <summary>
    /// Displays the view (modal) and returns a <see cref="DialogResult"/> value indicating how the view was closed.
    /// </summary>
    DialogResult ShowDialog();
}

したがって、ViewModelは本質的にcontrolsencapsulatingのポイントまでのビューであり、これにより、独自のDialogResult列挙(WinFormsが持っていた方法に非常に近い)。

ビューアセンブリ

このライブラリは、BLLアセンブリで定義されたインターフェイスを実装するPLLアセンブリのViewModelを参照および使用するため、ここには矢印がありません。BLLへの依存関係もありますが、間接的にのみ使用されます。このライブラリは、WPF/XAMLウィンドウとコントロールを定義することにより、Viewを提供します。

IValueConverterと他のMarkupExtensionの実装は別として、このライブラリにはC#コードは多くありません。ViewModel実装へのハード参照はすべてXAML d:DataContext {d:DesignInstance...}宣言。

では、何が問題なのですか?

私は、PLL /ビューを含む依存関係の方向性が間違いなく好きです。 BLLの依存関係がゼロであるという事実は、ライブラリが再利用でき、必要になった場合にWebUIを思い付くことができることを意味します(そして、結局は起こります...)。DAL/BLLデカップリングは、データモデルの一部がWebサービス、一部のクラウド、またはその他のものからのものである-ビジネスロジックはそれほど重要ではなく、それは良いことです。

私が抱えている主な問題は、抽象的なファクトリーをたくさん書いていて、それらをさらに実装していることに気づき、これらの抽象化を常に再利用しているわけではない(つまり、匂いがしている)のですが、私はいつでも好きなようです私自身が「動的」な依存関係をどのように注入できるか、答えはほぼ自動的に-「抽象ファクトリーが実行します!」...これはおそらく正しいですが、ほとんどすべてが「動的」依存関係!

public class SomeViewFactory : IViewFactory
{
    public IView Create(IViewModel viewModel)
    {
        return new SomeWindow {DataContext = viewModel};
    }
}

public class SomeRepositoryFactory : DataRepositoryFactory<SomeEntity>
{
    public IRepository<SomeEntity> Create(DataContext context)
    {
        return new SomeRepository(context);
    }
}

私は抽象的なIFunctionalityFactoryさえ持っています:

public interface IFunctionalityFactory
{
    IFunctionality Create();
}

public class SomeFunctionalityFactory : IFunctionalityFactory
{
    private readonly IFunctionalityAuth _auth;
    private readonly ISomeBusinessLogic _logic;

    public SomeFunctionalityFactory(IFunctionalityAuth auth, ISomeBusinessLogic logic)
    {
        _auth = auth;
        _logic = logic;
    }

    public IFunctionality Create()
    {
        var canExecute = auth.IsCurrentUserAuthorised(typeof(SomeFunctionality).FullName);
        return new SomeFunctionality(canExecute, _logic);
    }
}

リポジトリからDALのサービスやモデル、PLL/Viewライブラリのビューやビューモデルに至るまで、ほとんどすべてのファクトリが存在するのは正常ですか?今のところ、プロジェクトはかなり小さく、私はまだこのプロジェクトに取り組んでいる唯一のプログラマです。新しい機能の実装に関しては、SOLIDおよびできるだけシンプルなアーキテクチャが欲しいです。

これは、DI/IoCを実装する私の2番目のプロジェクトにすぎません。言うまでもありませんが、歩く前に実行(および泳ぎ、さらには飛行さえ)しようとしています...

表示されていないヒントや問題はありますか?Abstract Factoryを悪用していますか?他にどのようにビューをインスタンス化できますか?ユーザーの裁量で、[ほぼ]同一のデータベースの任意のセットに接続できるデータモデルを他にどのように作成できますか?私はMVVMを壊していることを知っています。自分の存在を単純化する可能性のあるパターンの見落としはありますか?

4
Mathieu Guindon

オブジェクトを作成するオブジェクト ...オブジェクトを作成するだけのほうがいいのではないですか?

Ninjectでは、非常に簡単な方法でこれを行うことができます。

// Ninject module
Bind<IRepository<SomeEntity>>().To<SomeEntityRepository>();

// injected class
public class SomeService
{
  public SomeService(IRepository<SomeEntity> repository)
  {
    // ...
  }
}

// get instance of injected class
var someService = kernel.Get<SomeService>();

SomeViewFactoryなどの基本的なコンパイル時の既知の制約に基づいてオブジェクトを作成している場合、抽象ファクトリーの利点IoCコンテナーに加えてがわかりませんおよびSomeRepositoryFactoryの例。

SomeFunctionalityFactoryの動的な性質はより議論の余地がありますが、それを考えるとき、オブジェクトをインスタンス化する必要がありますか組み込みの概念現在のユーザーが使用できますか?オブジェクトにユーザー認証を評価させるほうが理にかなっていると思いませんか機能が呼び出されたとき

4
guillaume31