インターフェースじゃなければ出来ない事

##はじめに

その昔DelphiやJavaを使っている時に、インターフェースの理解がモヤモヤしている時期がありました。
ググっていても、ふと気付くと**何故かCOMの話**になっていたりと、説明を読んでも良くわからない、メリットがいまいちピンと来ないという状態でした。
そして実際にインターフェースがなくても特に困っていなかったので、その頃の私には必要ないものだったのでしょう(※)。

※今振り返ると「あそこは使えたな」という事もあったりしますが(^^;

当時、一番しっくりこなかったのが、

```pascal:＜Delphi＞
var
  x: IInterface; //インターフェースを代入できる？？？
begin
  ...
```

...のように「**変数xにインターフェースが代入できる**」という表現でした。
<font color="Red">**実装のないインターフェースを代入できるって、どういう事！？**</font>　...と混乱していました。

##前提

多重継承NGでインターフェースが使える言語なら、大体当てはまると思います。
最近はC#の人なので以降はC#で記述しますが、Delphi、Java等も考え方は同じかと。

##インターフェースじゃなければ出来ない事...？

そんな訳で、自分設計のクラスを使っている分には別にインターフェースがなくても全然困ってなかった私ですが...

ある時、<font color="Red">**他人設計のクラスを魔改造**</font>する必要が生じました。

具体的には.NETのWinFormsのコントロール(MS製やGrapeCity製だったり)に、カーソルキー(←↑↓→)移動の機能を追加する事になったのです(※)。

![20210713_qiita_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954879/a1b422e9-cf73-a730-7361-821ee0591810.png)

※縦横に並んだ入力コントロールから、Tab移動以外で思った方向に進めるための機能です。
　Windowsアプリの流儀から外れるのは百も承知ですので、その点へのご批判はナシでお願い致します。

TextBox、ComboBox、CheckBox等を継承して、カーソルキーの移動先のコントロールを設定して移動できるようにしました。

```plantuml
@startuml
scale 1.1
title ＜インターフェースなし＞
TextBox <|-- XXXTextBox
ComboBox <|-- XXXComboBox
@enduml
```

```C#:＜C#＞継承クラス
    public class XXXTextBox : TextBox
    {
        [Browsable(true)]
        [Description("左カーソルを押した時の移動先を設定します。")]
        [Category("移動先")]
        public Control ToLeft { get; set; }
        ...

        //こんな感じで各コントロールを継承して
        //上下左右の移動先を記憶、押された方向に進める

        //C#なのでプロパティ使ってますが、
        //他言語なら各setter & getter を書けば良いです
```

因みに移動先の"Control"には、今回の魔改造コントロール以外も設定可能にしています。

> 視覚的に表現されるコンポーネントであるコントロールの基本クラスを定義します。
> (MSのControlクラスの説明より引用)

ここまでなら、別にインターフェースがなくても困りませんでした。

##夜明け前...

各継承コントロールに移動する機能を実装してヤレヤレと思っていた所、今度は<font color="Red">**無効な項目をスキップして進めたい**</font>という仕様追加が発生...

![20210713_qiita_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954879/88c49982-9df1-a8af-e233-9a7bb44694f7.png)

> 項目Bで↓を押した時、項目Eが無効なら項目Gに進めたい！

そこで、インターフェースに開眼していない私は、

「移動先が無効な場合、条件分岐で移動先が魔改造コントロールなら、更にその先を追って...」

...とか考えていたのですが。

「あ、これって**魔改造コントロールの種類が増える度にメンテしないとダメ**なヤツだ...」

```C#:＜C#＞魔改造コントロールを個別に判定...
    Control toDown = null;

    if (ctrl is XXXTextBox textBox) //魔改造コントロール個別に判断、
    {
        toDown = textBox.ToDown; //個別に取得
    }
    else if (ctrl is XXXComboBox comboBox) //魔改造コントロール個別に判断、
    {
        toDown = comboBox.ToDown; //個別に取得
    }
    ... //新しい魔改造コントロールが増える度に要対応...
    
    //※C#の場合、リフレクションで判定＆取得って手もあるけど(^^;
```

...と思った瞬間に、<font color="Red">**ここがインターフェースの出番なのかっ！**</font>とビビッと開眼！

##開眼！インターフェースじゃなければ出来ない事！

開眼した私は下記のインターフェースを作成、

```C#:＜C#＞インターフェース
    public interface IXXXControl
    {
        Control ToLeft { get; set; }
		
        //こんな感じで上下左右を記憶するインターフェースを作成

        //C#なのでプロパティ使ってますが、
        //他言語なら各setter & getter を書けば良いです
```

各継承コントロールにインターフェースを追加。

```C#:＜C#＞継承クラス
    public class XXXTextBox : TextBox, IXXXControl
    {
        [Browsable(true)]
        [Description("左カーソルを押した時の移動先を設定します。")]
        [Category("移動先")]
        public Control ToLeft { get; set; }
		
        //クラス宣言にIXXXControlを書き足しただけ。
        //インターフェースの内容は元々実装してあるので無問題。
```

```plantuml
@startuml
scale 1.1
title ＜インターフェースあり＞
Class TextBox
Class ComboBox
Interface IXXXControl
TextBox <|-- XXXTextBox
ComboBox <|-- XXXComboBox
IXXXControl <|.. XXXTextBox
IXXXControl <|.. XXXComboBox
@enduml
```

こうしておくと先の判定も、

```C#:＜C#＞魔改造コントロール用インターフェースの実装で判定
    Control toDown = null;

    if (ctrl is IXXXControl xxxCtrl)
        //新しい魔改造コントロールが増えても無問題！
    {
        toDown = xxxCtrl.ToDown; //IXXXControlの実装が保証されている！
    }
    
```

...で魔改造コントロールの判定が出来てメンテナンスフリーになるやん！

##まとめ

という事で<font color="Red">**大分遅まきながら**</font>インターフェースにしか出来ない事が腹落ちした瞬間でした。

<font color="Red">**自分が直接手出しできないクラスツリーに介入して、インターフェース単位でグルーピングできる！**</font>

昔しっくりこなかった下記コードも、今はすっかり馴染むようになりました！

```pascal:＜Delphi＞
var
  x: IInterface; //×インターフェースを代入できる？？？
                 //〇インターフェースを実装したインスタンスを代入できる
                 //　＆インターフェースの機能が使える
                 //　(インターフェースにない機能は使えない)
begin
  ～
```

※近年Delphiを触る機会はないですが...(^^;

個人的にオブジェクト指向の中で、インターフェースの理解が一番引っかかった部分でした。
同じようなモヤモヤを抱えている方の参考になれば幸いです！

####更新
2021.07.15 PlantUMLのイメージを、画像からMarkdown記述に変更