6
3

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-07-15

はじめに

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

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

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

<Delphi>
var
  x: IInterface; //インターフェースを代入できる???
begin
  ...

...のように「変数xにインターフェースが代入できる」という表現でした。
実装のないインターフェースを代入できるって、どういう事!? ...と混乱していました。

前提

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

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

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

ある時、他人設計のクラスを魔改造する必要が生じました。

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

20210713_qiita_01.png

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

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

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

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

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

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

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

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

夜明け前...

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

20210713_qiita_02.png

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

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

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

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

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

<C#>魔改造コントロールを個別に判定...
    Control toDown = null;

    if (ctrl is XXXTextBox textBox) //魔改造コントロール個別に判断、
    {
        toDown = textBox.ToDown; //個別に取得
    }
    else if (ctrl is XXXComboBox comboBox) //魔改造コントロール個別に判断、
    {
        toDown = comboBox.ToDown; //個別に取得
    }
    ... //新しい魔改造コントロールが増える度に要対応...

    //※C#の場合、リフレクションで判定&取得って手もあるけど(^^;

...と思った瞬間に、ここがインターフェースの出番なのかっ!とビビッと開眼!

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

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

<C#>インターフェース
    public interface IXXXControl
    {
        Control ToLeft { get; set; }

        //こんな感じで上下左右を記憶するインターフェースを作成

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

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

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

        //クラス宣言にIXXXControlを書き足しただけ。
        //インターフェースの内容は元々実装してあるので無問題。

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

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

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

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

まとめ

という事で大分遅まきながらインターフェースにしか出来ない事が腹落ちした瞬間でした。

自分が直接手出しできないクラスツリーに介入して、インターフェース単位でグルーピングできる!

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

<Delphi>
var
  x: IInterface; //×インターフェースを代入できる???
                 //〇インターフェースを実装したインスタンスを代入できる
                 // &インターフェースの機能が使える
                 // (インターフェースにない機能は使えない)
begin
  

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

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

更新

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

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3