インターフェースの何が嬉しいのか

# これはなに？
- 自分が初心者だったころ、インターフェースの理解はさっぱりだった。
- 同じように悩める初心者がいたら、理解を助ける手伝いをしたい。
- 概念の話ではなく、インターフェースがあると具体的に何が嬉しいのか。

# 誰に読んで欲しいの？
- インターフェースの概念はわかった、使い方もわかった、でも**使い道**がわからない人

# そもそもインターフェースって？
インターフェースは機能の実装を強制する仕組みです。
このインターフェースを継承するクラスは○○の機能を持ってるよ、ということを**保証**します。

参考：
http://ufcpp.net/study/csharp/oo_interface.html
https://qiita.com/IganinTea/items/e1d35db0a14a84bda452

# インターフェースを使うと嬉しいこと

## 便利な構文が使える
### IDisposable

私が一番よく使っている標準クラスライブラリのインターフェースはIDisposableです。
このインターフェースを継承すると`Dispose`の実装が強制されます。

`Dispose`の実装が強制されて何が嬉しいかというと、`using`ステートメントを使用することができます。
そして`using`ステートメントが利用できるとアンマネージリソース[^1]を扱うときにリソースの解放忘れを気にしなくて良くなります。

#### 利用例

usingを使う代表的な例がStreamReaderクラスです。
StreamReaderの親クラスであるTextReaderがIDisposableを継承しています。

```csharp
using System.IO;

using (StreamReader stream = new StreamReader("<File Path>")) {
    Console.WriteLine(stream.ReadToEnd());
}
```

ちなみに上記のコードは以下のコードと等価です。

```csharp
using System.IO;

StreamReader stream = new StreamReader("<File Path>");
try {
    Console.WriteLine(stream.ReadToEnd());
} finally {
    if (stream != null)
        stream.Dispose();
}
```

#### 実装例
IDisposableを継承してテキストファイルを表すクラスを実装すると以下のようになります。
（全く意味ないクラスだけど無理やり作ってみた）

```csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace ConsoleApp {
    public class TextFile : IDisposable {

        private StreamReader stream;

        public TextFile(string path) {
            stream = new StreamReader(path, Encoding.UTF8);
        }

        public IEnumerable<string> ReadLines() {
            string line;
            while((line = stream.ReadLine()) != null) {
                yield return line;
            }
        }

        public void Dispose() {
            stream.Close();
        }
    }
}
```

Open/Closeという概念があるクラスの場合、IDisposableを継承しusingステートメントを利用できるようにしておくと分かりやすく堅牢になるため良いです。  

後述の`IEnumerable`/`IEnumerable<T>`インターフェースを使うと、`foreach`ステートメントを利用できたりもします。
自作のコレクションなんて使ったこと無いけど。。。


## 余計なことは気にしなくていい

### Linqに見るインターフェースの活用

みなさんLinq使ってますか？
私はLinqの為にC#を使っていると言っても過言ではないくらいLinq大好きです。
Linqで`Select`とか`Where`とか使うと、返ってくるのは`IEnumerable<T>`ですよね。ここでは`IEnumerable<T>`が返ってくると何が嬉しいのか考えてみます。

まずListを`Select`した例を見てみたいと思います。

```csharp
List<string> list = new List<string>() { "hoge", "fuga", "piyo" };
IEnumerable<string> results = list.Select(x => x);
```

Listに対して`Select`を通しただけの文ですが、この時resultsの実体は何なのでしょうか？  
正解は`WhereSelectListIterator<TSource, TResult>`クラスのインスタンスです。
http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,37c6daad6e403a3b,references
これがListではなく配列だった場合、`WhereSelectArrayIterator<TSource, TResult>`が返ってきますし、
これが`Select`ではなく`Where`だった場合、`WhereListIterator<TSource>`が返ってきます。

この時`Select`の実装はこんな感じです。

>```csharp
>public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) {
>    if (source == null) throw Error.ArgumentNull("source");
>    if (selector == null) throw Error.ArgumentNull("selector");
>    if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Select(selector);
>    if (source is TSource[]) return new WhereSelectArrayIterator<TSource, TResult>((TSource[])>source, null, selector);
>    if (source is List<TSource>) return new WhereSelectListIterator<TSource, TResult>((List<TSource>)source, null, selector);
>    return new WhereSelectEnumerableIterator<TSource, TResult>(source, null, selector);
>}
>```
http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,5c652c53e80df013,references

様々なクラスのインスタンスがreturnされますが、戻り値は`IEnumerable<T>`の為これらのインスタンスは`IEnumerable<T>`としかふるまいませんし扱われません。つまりインスタンスが何であるかは**気にしなくて良い**のです。
私たちが`Select`や`Where`に期待することは`IEnumerable<T>`を継承するコレクションから呼び出せて、結果を`IEnumerable<T>`として戻すこと、それだけで**十分**なのです。

> もちろんインデクサを使ったアクセスや要素の追加・削除がしたい場合は`IEnumerable<T>`では不十分です。
その場合は`ToList()`してListとして扱うと良いです。

また`IEnumerable<T>`を継承さえしていればインスタンスは何でも良い状態は変化に強いと言えます。仮に`Select`の実装に変更があり、`WhereSelectEnumerableIterator<TSource, TResult>`が全く違うクラスに置き換わったとしても、利用する側に影響は無いでしょう。


### 業務システムで使うインターフェース

もう一つ例を見てみます。

業務システムで受注データをファイルから読み取り、DBへ格納するという処理は割と一般的です。
そして受注データのファイルが取引先によって形式もフォーマットもバラバラということもまた一般的です。
この処理を実装してみたいと思います。

|取引先|ファイル形式|
|---|---|
|A社|テキスト（固定長）|
|B社|CSV|
|C社|XML|

まずDBへ格納するパラメータは商品（ItemId）・納期（DeliveryDate）・数量（Quantity）の3つとしましょう。
それを1件分保持する構造体を作ります。

```csharp
public struct Order {

    public Order(string itemId, DateTime deliveryDate, int quantity) {
        ItemId = itemId;
        DeliveryDate = deliveryDate;
        Quantity = quantity;
    }

    public string ItemId { get; private set; }
    public DateTime DeliveryDate { get; private set; }
    public int Quantity { get; private set; }
}
```

そして受注ファイルのインターフェース`IOrderFile`を作成します。
ファイルの内容（受注データ）を列挙して返す`Read`メソッドと、読み終わったファイルを削除する`Delete`メソッドをふるまいとして定義します。

```csharp
using System.Collections.Generic;

namespace ConsoleApplication1 {
    public interface IOrderFile {
        // 受注データを列挙して返す
        IEnumerable<Order> Read();
        // ファイルを削除する
        void Delete();
    }
}
```

受注ファイルの具象クラス`OrderFileFromA`・`OrderFileFromB`・`OrderFileFromC`も作成します。
各クラスでそれぞれのメソッドに対し個別の処理を実装をします。

コンストラクタでファイルパスを受けてローカル変数として保持していますが、`Read`メソッドで必要になる（はず）です。

```csharp
using System.Collections.Generic;

namespace ConsoleApplication1 {

    public class OrderFileFromA : IOrderFile {
        private string filePath;
        public OrderFileFromA(string filePath) {
            this.filePath = filePath;
        }
        public IEnumerable<Order> Read() {
            // 固定長テキストの処理
        }
        public void Delete() {
            // ファイル削除処理
        }
    }

    public class OrderFileFromB : IOrderFile {
        private string filePath;
        public OrderFileFromB(string filePath) {
            this.filePath = filePath;
        }
        public IEnumerable<Order> Read() {
            // CSVファイルの処理
        }
        public void Delete() {
            // ファイル削除処理
        }
    }

    public class OrderFileFromC : IOrderFile {
        private string filePath;
        public OrderFileFromC(string filePath) {
            this.filePath = filePath;
        }
        public IEnumerable<Order> Read() {
            // XMLファイルの処理
        }
        public void Delete() {
            // ファイル削除処理
        }
    }
}
```

前提となるコードが多いですが頑張りましょう。

`IOrderFile`の具象クラスを3つ定義しましたが、これらの生成（`new`）はどうやってやりましょう？

それぞれのファイル形式が異なる（拡張子で判別できる）ので、ファイルパスから判別できます。[^2]
（ファイル名から取引先が判別できればそれでも良いと思います。）
ファイルパスをインプットして、具象クラスをアウトプットする`Factoryクラス`が今回は適当です。[^3]

```csharp
using System;

namespace ConsoleApplication1 {
    public static class IOrderFileFactory {

        public static IOrderFile Create(string filePath) {
            // ファイルパスで判別して具象クラスを返す。
            if (<判別>) {
                return new OrderFileFromA(filePath);
            } else if (<判別>) {
                return new OrderFileFromB(filePath);
            } else if (<判別>) {
                return new OrderFileFromC(filePath);
            } else {
                throw new ApplicationException();
            }
        }
    }
}
```

くどいようですが、具象クラスのインスタンスをreturnしても戻り値はインターフェースという点がポイントです。

最後にMainクラスです。ファイルパスはargsから受け取る想定です。

```csharp
using System.Collections.Generic;

namespace ConsoleApplication1 {
    class Program {
        static void Main(string[] args) {

            IOrderFile file = IOrderFileFactory.Create(args[0]);

            IEnumerable<Order> orders = file.Read();

            foreach(var order in orders) {
                // orderをDB登録する処理
            }

            file.Delete();
        }
    }
}

```

Mainクラスでは`IOrderFile`の具象クラスは登場せず、インターフェースのみで動いています。
必要なのは`Read()`と`Delete()`の機能のみであり、ファイルがテキストだろうがCSVだろうが関係ないのです。

そして具象クラスに依存するコードは全て`IOrderFileFactory`にあります。
当然取引先が増えることも減ることもありえるでしょう。しかしその際は`IOrderFileFactory`だけの変更で済みます。変更に対する影響範囲が限定されることは大きなメリットの一つでしょう。


# インターフェースを使う癖をつけよう

今までインターフェースを使ったことが無い人には`IDisposable`がオススメです。利点もわかりやすいし、実装も簡単です。

インターフェースは使おうと思わないと使わない機能なので、意識して使っていく気持ちが大事です。

なんでもかんでもインターフェースにすれば良いというものではありませんが、「これってインターフェースにできないかな？」と考える癖をつけてみましょう。



# 最後に

「インターフェースに対してプログラミングしろ」という原則は特に目新しいものでもなく、本稿の結論も全く同じです。

しかし車とか林檎とかの概念で説明されても正直わかりません。
私はデザインパターンを勉強する中で実際に手を動かしやっと利点がつかめた気がしました。

初心者・初級者の方のふわっとした理解が少しでも固まる手助けになれば幸いです。

[^1]: ファイルとかDBコネクションとか
[^2]: 拡張子が嘘ついてなければ
[^3]: SimpleFactoryパターン