以前「【App. Design】Dependency Injectionと依存関係」で、説明したDependency Injectionを実装してみます。
「【App. Design】プロジェクトを分け階層型のアーキテクチャにする」で作成したプログラムに、インフラ層を追加しましょう。
「ファイル入力」というボタンを追加し、このボタンをクリックすると、ファイルを入力して画面の「TextBox1」に反映されます。
ただし、今回は説明をシンプルにするため実際のファイルを読み書きはせず、ダミーの文字列を表示します。
動かすと次のアニメーションのようになります。

プログラム構造の全体イメージ
プログラミングでは、外部の環境に左右されやすい処理は分離して、重要なコードの独立性を高めます。プログラムの品質が向上するからです。
外部の環境に左右されやすい処理とは、ファイル入出力や外部機器との通信などが該当し、プログラミングではインフラストラクチャ(インフラ)と呼ばます。
次の図を見てください。

前回まででソリューションの構造は真ん中の状態になっていますが、ここに「Infrastructure」というプロジェクトを追加して右側のようにします。
この「Infrastructure」プロジェクトがファイル入出力のコードを置く場所です(今回は説明をシンプルにするため実際にはファイルを読み書きせずダミーのコードにします)
それでは、実装してみましょう。
「Domain」プロジェクトの改変
まず「Domain」プロジェクトの改変です。
次のようになります。
public class ValueObject1 : NotificatonBase
{
IInterface1 _iInterface1;
public ValueObject1(IInterface1 iInterface1)
{
_iInterface1 = iInterface1;
}
private string _prop1 = "";
public string Prop1
{
get => _prop1;
set
{
_prop1 = value;
RaisePropetryChanged(nameof(Prop1));
}
}
public void GetData()
{
Prop1 = _iInterface1.Read();
}
}
public interface IInterface1
{
string Read();
}
インターフェイス「IInterface1」を定義し、その中に「Infra1」のメソッド「Read」を記載します。
「ValueObject1」クラスでは、コンストラクタを明記し引数でインターフェイスを実装したオブジェクトを受け取るようにします。
そして、メソッドの中では受け取ったオブジェクトで「Read」を呼び出すのです。
「Read」の戻り値は、プロパティに代入します。
プロパティのsetterで通知を発行するので、画面にも反映されます。
つまり、「ValueObject1」のコンストラクタの引数に、依存オブジェクト(Dependency)である「Infra1」のオブジェクトを注入(Injection)しているわけです。
プロジェクトの追加とインターフェイスの実装
次に、「Infrastructure」という名前でプロジェクト追加しましょう。
プロジェクトの追加方法は「こちら」で説明した通りです。
「Infrastructure」から「Domain」にプロジェクト参照を追加します。
「WpfApp1」から「Infrastructure」へもプロジェクト参照を追加します。
そして、「Infrastructure」プロジェクトにクラスを追加して、インターフェイスを実装します。
ファイル名は任意でかまいませんがここでは「Infra1.cs」という名前にしています。
using Domain;
public class Infra1 : IInterface1
{
public string Read()
{
return "テスト用のダミー・データ";
}
}
本来、Readメソッドの中には、ファイルを読み込むコードを書きますが、今回はテーマからそれるので単純に文字列を返すコードにしておきます。
Dependency Injection
次に「ViewModel」を次の通りに変更します。
using Domain;
using Infrastructure;
public class ViewModel
{
public ViewModel()
{
var infra1 = new Infra1();
_valueObject1 = new(infra1);
}
private readonly ValueObject1 _valueObject1;
public ValueObject1 ValueObject1 { get { return _valueObject1; } }
}
「ValueObject1」のコンストラクタで引数を受け取るようにしたので、生成時に引き渡すわけです。
「ViewModel」にもコンストラクタを明記し、まず、その中で「Infra1」クラスを生成します。
それを「ValueObject1」クラスの生成で引数として渡します。
画面にボタンを追加
画面にボタンを追加し、ボタンをクリックしたら、「GetData」メソッドが呼び出されるようにします。
<StackPanel>
~略~
<Button Content="ファイル入力" x:Name="Button2" Click="Button2_Click" Margin="5,5,5,5"/>
</StackPanel>
private void Button2_Click(object sender, RoutedEventArgs e)
{
App.MyViewModel!.ValueObject1.GetData();
}
まとめ
以上で、Dependency Injectionという方法を使って、依存の逆転を行うことができました。
これでアプリのコアとなる「Domain」プロジェクトの独立性を保ったまま、ファイルの入力ができる構造になったわけです。
ファイルの入出力は外部の環境に左右されることが多い箇所なので、切り離してコアの安全性を確保しているわけです。
コメント