【C#】完全コンストラクタパターンの意義と活用例


　WPF、WinForms、最近ではMauiとかあらゆるフレームワークで活用出来るプログラミングパターンとして完全コンストラクタパターンがある。

　最初、なんでこんな実装をするのか分からなくて色々考えてみた。


単純な実装事例としてSubFormを使ってみる（前に使ったから）。
外観は以下を用意する。初学者はコピペでもしてほしい


# 外観
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145714/7a1286f9-6e08-3d77-5fd1-8c766598980e.png)


```C#:XAML
<Grid>
        <StackPanel Orientation="Vertical">
        <Button Content="Button"  Click="Button_Click" HorizontalAlignment="Left" Margin="55,79,0,0" VerticalAlignment="Top" Height="55" Width="150"/>
        <Button x:Name="AllWomanAresheep2"  Content="Button"  Click="AllWomanAresheep2_Click" HorizontalAlignment="Left" Margin="55,79,0,0" VerticalAlignment="Top" Height="55" Width="150"/>
        </StackPanel>
        <TextBox HorizontalAlignment="Center" x:Name="SubTextBox"  Margin="0,75,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="276" Height="63"/>

    </Grid>
```

# コードビハインド（実装側）
```C#:SubFormClass
public SubForm(string _target)
        {
            InitializeComponent();
            target = _target;
        }
readonly string target;
//またはsetterを抜く；
string target { get;}

```

**readonly が肝である。** こうする事でNew時のみその変数に値を入れることが出来る。

これはSubFormなので、ManForm側でも全く同一の外観を用意する。SubFormと同じで構わない。

form間の値の受け渡し方法なんかも纏めてみた
　https://qiita.com/EndOfData/items/4a86424ffd0cd9ee3e1a

# 呼び出し側（MainForm側）

    private void Button_Click(object sender, RoutedEventArgs e)
        {
           SubForm sf = new SubForm("羊乃モコはかわいい");
            sf.Show();

            sf.SubTextBox.Text = sf.target;       

        }  
　
本当はもっと単純な文字列を扱うだけのクラスを用意すべきだったかもしれません。
これを実行すると以下のようになります。

※これはGIF画像です。![11-09-12-751_Harua.mp4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145714/ea23b844-b6d0-64d7-e009-a0c2ec7e4865.gif)
　


# なんでこんな面倒な実装をするの？
　~~知りません~~　ではお話にならないからきちんと説明します。
　外部のクラスのメソッドを呼び出したりすときに、そのクラス内の変数にアクセスしたいケースがあったとします（そんな事するよりは変数だけのクラスやインターフェースを定義する事が多いかも知れない）

 SubFormに入れた文字列は顧客側のインスタンスでのみ扱いたいため、継承先や外部メソッドやDelegateなんかで呼ばれた場合に自由に変更されると面倒なことになるのが想定される訳です。


## 例えばこんなケース
　SubForm側に非ReadOnlyな変数を用意します。

```C#:SubForm
public string NonReadOnly_target;

```


```C#:MainForm(呼び出し側)

 private void Button_Click(object sender, RoutedEventArgs e)
        {
           sf = new SubForm("羊乃モコはかわいい");
            sf.Show();         

            ALL_sheeps.AllWomenAreSheep("羊乃モコはかわいい", sf);            
           //クラス内のCodeはAbstrautによる継承やintarface等で複雑に隠蔽が使われており
           //一見してどうなっているのかわからないという事にする

            sf.SubTextBox.Text = sf.NonReadOnly_target;
           //他のイベント実装などでも
           //意図しないタイミングで変更されたので出力結果が想定したものと違う　
        }

//クラスとメソッドを追加してみる　
        static class ALL_sheeps
        {
           public static void AllWomenAreSheep(string sheepString,SubForm sf)
            {
                sf.NonReadOnly_target = sheepString.Replace("かわいい", "2本足のひつじである");

            }
        }
```

　ReadOnlyじゃないし、呼び出し側で初期化してないから独立性が保たれないのでアクセス出来てしまう訳ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145714/2cf0f52d-6b35-0854-3875-98e3d2b6e2e8.png)


ReadOnlyなMenberを初期化する実装にすれば、こうした点は回避出来る訳です。
コメントアウトで挙げた例の他、**入れた値がクライアント側で更新されてなかった**とかはNewによる確実な初期化で防げますね。

もちろん、必ずしも実際にそんな手間を掛ける必要はないし逆に不便だという事もあるのでトレードオフの関係にあります。それでもCoding的には堅牢な仕様になるという事は覚えておいていいと思う。柔軟な運用をしましょうと個人的には思います。


# 完全コンストラクタパターンによるエラー原因の切り分け
 [自作のffmpegフロントエンド](https://qiita.com/EndOfData/items/b643638b176cd440c648)開発中に上手くいかなかった実装に完全コンストラクタパターンを使ってみた。それほど手間という訳でもないので上手く使えそうである。

　OpenFileDialogを複数のFileSelectorで使い回す実装をしてるんだけど、InitialDirectoryに渡す変数が上手く反映されないという問題の切り分けに。


```C#
internal class CommonOpenDialogClass
    {
        public CommonOpenDialogClass(bool _isFolder, string _initializeDirectory) 
        {
            isFolder = _isFolder;

    //ついでにNullだったときは例外を投げさせてみる
          if(string.IsNullOrEmpty(_initializeDirectory))
                throw new ArgumentException("Value is Null"); 
           else
                initializeDirectory = _initializeDirectory;

        }
     }

        //Perfect Construct Menber
        string initializeDirectory { get; }
        bool isFolder { get;  }


////呼び出し側
 CommonFileDialogResult MyResult()
{
     var ofc = new CommonOpenDialogClass(isFolder, ParamInterfase.InitialDirectory);
            
     var commons = ofc.CommonOpens();
}
```



アクセサの明示が無い場合は基本的にprivateである。
こう書いておくと、そのインスタンス内でどこからも変更できない事が保証される（尚且つオブジェクトの破棄もGCで行われるから更新も保証される）。なんなら明示的にGCさせてもいいだろう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145714/69020063-95fe-56e4-6a62-90d4e41796e2.png)

もともとPrivateなんでアクセスできないけど、これを外部に公開（Public）して使い回したくなった場合はより重要になってくる。

これをしてしまえば、とりあえず(メソッド書き間違いがない前提で)**他のイベントやメソッド呼び出し、デリゲート等は原因から除外出来る**。となれば後はコンストラクタに渡す外部管理用の変数が更新されてないとかそういう問題だろうという事になる。

このように、後で適切に原因を切り分けしてデバッグする際には非常に有用である。

後でやるぐらいなら最初からやってしまえという事にもなるだろう。

# 完全コンストラクタパターンで値を改変したいときは
そもそも改変を防ぐ目的で実装しているのでReadOnlyは外してはいけない。
値に何等かの処理をしたいときは外部公開用の変数を用意すればよい。


```C#:SubForm.cs
 public partial class SubForm : Window
    {
        public SubForm(string _target)
        {
            InitializeComponent();
            target　= _target;
        }
   
        public readonly string target;

        //外部公開用変数
        public string Public_target; 
    }

 class ALL_sheeps
        {
            public void AllWomenAreSheep(SubForm sf)
            {         
          //RaedOnlyな値を処理したい
           sf.Public_target = sf.target?.Replace("かわいい", "2本足のひつじである");               
            }
        }

```

# C#における完全コンストラクタパターンとは初期化の明示であるという点
　
 　実装方法によってはいちいちクラスをnewせずにインスタンスを使い回すといった事も可能なんですが、**これは新しいセッションであるという事を明確にする場合はNewすべき**です。

```C#
sf.target = "羊乃モコはかわいい"
```
 　より
  ```C#
 sf = new SubForm("羊乃モコはかわいい");
```
のほうが変数は隠蔽して抽象化できるし、直接アクセスは防げるしで、考えてみるとけっこう利点も多い訳ですね。
 　更に、内部変数と外部変数を``_target``で受け渡す事により、変数の使用範囲を明確にしている訳です。

  
 　これは複数ユーザーが使用したり、アクセスする時間帯が違うという前提の設計・サービスにおいては当然実装すべきであるとすら言えるでしょう。このようなケースにおいては、むしろ**インスタンスを使い回すような実装は非常に危険である**とも言えます。
  　


　実を言うと、一定の範囲ではインスタンスを使い回す実装の方が個人アプリ開発では便利だったりもするのですが、**実装が複雑化するほどに完全コンストラクタパターンの重要性は増していく**とも解釈できます。