インターフェイスの使用例

# はじめに

[抽象クラスの使用例](https://qiita.com/ats_y/items/6088aada15b22c2fef75)のインターフェイス版です。
インターフェイスの意味・定義の仕方は知っているけど、どんなときに使うのか・どんな良いことがあるのか。
その例を紹介します。

# いきなり結論。改造に強くなります。

抽象クラス・汎化・継承と同じで、改造に強くなります。
[抽象クラスの使用例](https://qiita.com/ats_y/items/6088aada15b22c2fef75)で紹介したプログラムにさらに改造要求が発生したことを例として紹介します。

### 改造対象のプログラム

まずは、[抽象クラスの使用例](https://qiita.com/ats_y/items/6088aada15b22c2fef75)で紹介したプログラムのおさらいです。

プリンターを管理するプログラムで、最初はインクジェットプリンターとレーザープリンターを管理してプリンター情報を一覧表示していました。
そこに、管理できるプリンターとしてドットインパクトプリンターを追加してくれという改造要件がきたので、抽象化（汎化・継承）で改造に強いプログラムを作りました。

クラス構成：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342629/9f8ee174-44e6-4dc6-38d4-0060116dae77.png)

メンバ変数mModelName(モデル名)とmPaper(用紙残量)を汎化、プリンター情報を返すgetPrinterInfo()関数を抽象化しています。

### 新たな改造要件

今回はさらに「管理するプリンターの種類に3Dプリンターを追加してそのプリンター情報も表示して欲しい」という要件がきたとします。

ドットインパクトプリンターを追加したときのようにAbstractPrinterを継承して…としてもできないことはないですが、3Dプリンターは紙に印刷するわけではないので、mPaper(用紙残量)は不要です。
使わないメンバ変数はプログラムを保守する際に邪魔なのでない方が良いです。

じゃあ、独立した3Dプリンタークラスを作るか。
それだと、[抽象クラスの使用例](https://qiita.com/ats_y/items/6088aada15b22c2fef75)の抽象クラスを使わないプログラムのように冗長になるし…。

ではどうすれば良いか。
インターフェイスで解決してみましょう。

### インターフェイスを利用して改造してみる
まず、インターフェイスを作ります。

```java

//
// プリンターインターフェイス
//
interface IPrinter{
    
    // 一覧表示用プリンタ情報を返す
    String getPrinterInfo();
}
```

プリンターインターフェイスIPrinterを実装する形で3Dプリンタークラスを作成します。

```java
//
// 3Dプリンター
//
class ThreeDPrinter implements IPrinter{

    // 材料容量
    private int mMaterial;
    
    // コンストラクタ
    public ThreeDPrinter(int material){
        mMaterial = material;
    }
    
    // 一覧表示用プリンタ情報を返すメソッド 
    public String getPrinterInfo(){
        
        return "[3D]開発中3Dプリンター" + " 印刷可能容量：" + mMaterial + "リットル";
    }
}
```

AbstractPrinterクラスにもプリンターインターフェイスIPrinterを実装します。

```java
//
// プリンターの抽象クラス
//
abstract class AbstractPrinter implements IPrinter{
    // 略
}
```

管理するプリンターを格納する配列の型をAbstractPrinterからIPrinterに変更します。

```java
    public static void main(String[] args) throws Exception {

        // 管理するプリンターを登録する。
        IPrinter[] printers = {
            new InkjetPrinter("Epson P1", 10),
            new InkjetPrinter("Canon C1", 20),
            new LaserPrinter("Xerox X1", 100),
            new LaserPrinter("Richo R1", 50),
            new LaserPrinter("Richo R2", 200),
            new DotimpactPrinter("NEC N1", 100),
            new DotimpactPrinter("Oki O1", 50),
            new ThreeDPrinter(5),
        };
        
        // 管理しているプリンターを一覧表示する。
        for(int i = 0; i < printers.length; i++ ){
            String printerInfo = printers[i].getPrinterInfo();
            System.out.println(printerInfo);
        }
    }
```

これだけ。
実行してみましょう。

```text:実行結果
[インクジェット]Epson P1 印刷可能枚数：10
[インクジェット]Canon C1 印刷可能枚数：20
[レーザー]Xerox X1 印刷可能枚数：100
[レーザー]Richo R1 印刷可能枚数：50
[レーザー]Richo R2 印刷可能枚数：200
[ドットインパクト]NEC N1 印刷可能枚数：100
[ドットインパクト]Oki O1 印刷可能枚数：50
[3D]開発中3Dプリンター 印刷可能容量：5リットル
```

できましたー！
[抽象クラスの使用例](https://qiita.com/ats_y/items/6088aada15b22c2fef75)のときと同じように管理する変数も増えず、表示する処理も変更せずにできました。
https://paiza.io/projects/wSSYJVnwbZfsI0vXHrsGjg

クラス構成：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342629/9f4c3d5e-d3ed-7331-adf3-890367410110.png)

# まとめ

インターフェイスも型なんですね。なので、変数の型として使えます。
**インターフェイス型の変数には、そのインターフェイスを実装しているクラスのオブジェクトを格納できます。**

```java
// 管理するプリンターを登録する。
IPrinter[] printers = {
    new InkjetPrinter("Epson P1", 10),
    // 略
    new ThreeDPrinter(5),
};
```

ThreeDPrinterクラスもAbstractPrinterから派生した各クラスもIPrinterインターフェイスを実装しているので、IPrinter型の変数にそのオブジェクトを格納できます。
そしてgetPrinterInfo()関数を定義しているIPrinter型の変数は、その中に入っているオブジェクトの詳しいことは知らないけどgetPrinterInfo()関数を実装していることだけは確かなのでそれを呼び出すことができます。

```java
// 管理しているプリンターを一覧表示する。
for(int i = 0; i < printers.length; i++ ){
    // printers[i]の中身は何かわからないけど、
    // printers[i]はIPrinter型なので  
    // getPrinterInfo()が実装されているのでそれを呼び出せる。
    String printerInfo = printers[i].getPrinterInfo();
    System.out.println(printerInfo);
}
```