公開日:1/3/2023 更新日:1/5/2025
こんにちは。Java を実務で使用して3年が経った筆者です。
今回、オブジェクト指向の重要な構成要素の一つである、インターフェースについて改めて学ぶ機会があったので記事にしました。
私自身そうでしたが、新人研修時代にはインターフェースを使う必要性について、いまいち腹落ちしていませんでした。
本記事では、具体的にどういった用途でインターフェースが活躍するのか紹介しようと思います。
同じように、インターフェースの使いどころが良く分かっていない人の参考になると幸いです。
JAVAのオブジェクト指向、特にinterface (インターフェース) の使いどころが分からない人
オブジェクト指向で指すインターフェースとは、オブジェクトの振る舞い(メソッド)のみを規定した設計書(クラスの雛形)のようなもの
1.interface Animal を作成
動物の雛形と、鳴き声のメソッドを用意しています。
実際の処理は定義せず、メソッド宣言のみ行います。 設計書と呼ばれる所以です。
public interface Animal {
String bark();
}
2.interface Animal を継承した class Dog と class Catを作成
Animal インターフェースを implements して犬と猫のクラスを作ります。
メソッドの具体的な処理を記述してオーバーライドします。
public class Dog implements Animal{
@Override
public String bark() {
return "ワンワン";
}
}
public class Cat implements Animal {
@Override
public String bark() {
return "ニャーニャー";
}
}
3.Animal に宣言されたメソッドを実行
Animal型の犬 or 猫 インスタンスを作成して、メソッドを実行します。
new した犬 or 猫 インスタンスを、Animal型(インターフェース)の変数 animal に格納しているのがミソです(多態性を利用)。
※もし、多態性(ポリモフィズム)が分からない人は以下のリンクの記事を参考にして下さい。
【Java】多態性 (ポリモーフィズム) の実用例を考えてみる
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
System.out.println(animal.bark());
}
}
例えば、以下のように new した犬型のインスタンスを犬型の変数 animal に格納しても動きますが、せっかくのインターフェースが台無しになります。
Dog animal = new Dog();
インターフェースという言葉の一般的な意味は以下となっています。
「インターフェース」は、「接点」「境界面」などの意味を持つ、英語の「interface」を由来とする言葉です。特にITの分野で使われることが多く、IT用語の「インターフェース」は、2つの異なる機器やシステム、ソフトウェア間で情報のやり取りがなされる際、その間をつなぐ規格や機能を指します。
今回の例でいう、2つの機器やシステムは、犬クラスと猫クラスになります。
インターフェースを介することにより、変数の型(Animal)を変更することなくクラスを繋ぎ変えて処理を変更することが可能になります。
↑ここの理解が重要かと思います。
将来的に例えば、馬クラスや鳥クラスなどが増えた際にも、インターフェースを介することで拡張が容易になりコードの可読性が向上します。
public class Main {
public static void main(String[] args) {
//猫のパターン
Animal animal = new Cat();
System.out.println(animal.bark());
}
}
例えば以下のようなコードを例にします。
animalType に従って、違う処理を実行したいニーズがあり、swich文で実現しています。
public class Main {
public static void main(String[] args) {
String animalType;
animalType = "dog";//実際は動的に変化
switch (animalType){
case "dog":
System.out.println("ワンワン");
//犬の処理が実際は何十行も存在
break;
case "cat":
System.out.println("ニャーニャー");
//猫の処理が実際は何十行も存在
break;
case "bird":
System.out.println("チュンチュン");
//鳥の処理が実際は何十行も存在
break;
}
}
}
お遊びのコードでなら十分ですが、大規模になると条件分岐と処理内容が一気に膨らみ可読性が下がってしまいます。
保守や変更が難しくなってしまった巨大なクラスは、関心事の小さなクラスに分割するのが得策です。
この課題解決に役立つのが interface なのです! interface を使用することにより、処理の切替が容易になり条件分岐も不要になります。
これから上記コードを簡単にリファクタリングしていきます。
↑interface とは で紹介した Animal インターフェイスや 犬と猫クラスを再利用します。
今回、鳥クラスも Animal インターフェースを implements して作成します。
public class Bird implements Animal {
@Override
public String bark() {
//実際には処理が何十行も存在
return "チュンチュン";
}
}
最終的にリファクタリングされたMainクラスは以下のようになります。
public class Main {
public enum AnimalType {
DOG,
CAT,
BIRD
}
public static void main(String[] args) {
final Map<AnimalType,Animal> map = new HashMap<>();
map.put(AnimalType.DOG,new Dog());
map.put(AnimalType.CAT,new Cat());
map.put(AnimalType.BIRD, new Bird());
//mapを使用して処理の切替が1行で実現
Animal animal = map.get(AnimalType.DOG);
//Animalインターフェイスの各実装クラスに個別ロジックを実装
System.out.println(animal.bark());
}
}
swich文を使用せずに処理を切り替えるために、map を使用します。
map を使用することで、AnimalTypeをキーに呼び出したいAnimalインターフェイスの実装クラスのインスタンスを取り出すことが可能になります。
わずか1行で処理の切替が出来て、個別ロジックも各実装クラスに記載されているので、コードの可読性が飛躍的にアップしました。
また、インターフェイスを使用することで実装漏れを防ぐなどの効果も期待できます。
本記事を通じて、少しでもインターフェイスの理解が深まれば幸いです。
ストラテジーパターンについては以下の書籍の6章から学ばせていただきました。
良いコード/悪いコードで学ぶ設計入門
仙塲大也(著)