どうも。つじけ(tsujikenzo)です。このシリーズでは、2022年5月に発売されました書籍「良いコード/悪いコードで学ぶ設計入門」についてお送りします。今日は3回目です。
前回のおさらい
前回は、「switch文の重複とクラスの肥大化」をお送りしました。
今回は、「インターフェイスの概念」をお届けします。
今日のアジェンダ
- 抽象化とインターフェイス
- 型とポリモーフィズム
- if文の排除
抽象化とインターフェイス
「四角形」や「円」などの、具体的な形を抽象化すると「図形」と言えます。
今後、具体的な形がいくら増えても、この構造に変化は起きません。
そして、「具体的な形」を「抽象化」しているわけですから、 「図形」は安定 しています。
さらに、具体的なそれぞれの形は独立しており、安定しています。
最終的に、抽象化した「図形」をプログラムから使うことで、プログラム全体が安定します。
こいつのことを「インターフェイス」と呼びます。
「インターフェイスとは、異なる型を同じ型として利用できるようにするもの」とは、 「具体的な形(四角形や円など)を、(抽象化した)図形として利用しよう」 という意味なのです。
型とポリモーフィズム
「型」とは「扱い方」です。「その値をどんな扱い方するんですか?」という問いに答えたものです。
ソースコード中の「3」という値に対して、どんな扱い方をするの?と問いかけたとき、 「文字列として扱うよ」と答えたものが「文字列型」 です。
リアルな世界の「〇」というものに対して、「円として扱うよ」と答えたのが「円型」です。
しつこいですが、「あなたが、これから扱おうとしてる〇だか□だかわかりませんが、それ、なんて言って扱うんですか?」に答えたのが「図形型」です。
抽象クラスであるインターフェイスには、処理の中身 {} を持たないメソッド(具象から抽象化したメソッドだからです)を定義します。
interface Shape{
double area();
}
具象クラスと実装
いっぽう、四角形や円などのクラスを、「具象クラス」と呼びます。
具象クラスでは、インターフェイスの抽象メソッドの中身を実装します。これは、メソッドのオーバーライドです。
/** Rectangleクラス */
class Rectangle implements Shape {
//省略
// オーバーライドによる実装
public double area() {
return width * height;
}
}
if文の排除
以上で2つのクラスが定義できました。
- 抽象メソッドをもつ、抽象クラス(インターフェイス)の「図形」
- 具象メソッドをもつ、具象クラスの「長方形」
そして、長方形と図形は具体と抽象の関係にあるので、「長方形を(あたかも)図形型として扱える」というのがポリモーフィズムです。
多態性とか多相性と、呼ばれる仕組みです。
/** 実行用エントリポイント */
class Sample6_24 {
public static void main(String[] args) {
Shape shape =new Rectangle(10, 10); //長方形クラスのインスタンスを図形型として扱う
System.out.println(shape.area()); // 図形型なので、shape.area()で呼び出せる。A.100
shape = new Circle(10); //円クラスのインスタンスを図形型として扱う
System.out.println(shape.area()); // 図形型なので、shape.area()で呼び出せる。314.1592653589793
}
}
ポリモーフィズムによって、すべての具体的な型たちを「図形型」として扱えるようになったので、もはや型判定をする必要がなくなります。
このように、型判定のif文を無くすことができます。
//どこかに書かれているだろう処理
class ShowArea {
static double showArea(Shape shape) {
return shape.area();
}
}
interface Shape {
double area();
}
/** Rectangleクラス */
class Rectangle implements Shape {
private final double width;
private final double height;
Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
// オーバーライドによる実装
public double area() {
return width * height;
}
}
/** Circleクラス */
class Circle implements Shape {
private final double radius;
Circle(double radius) {
this.radius = radius;
}
// オーバーライドによる実装
public double area() {
return radius * radius * Math.PI;
}
}
/** 実行用エントリポイント */
class Sample6_26 {
public static void main(String[] args) {
Shape shape = new Rectangle(10, 10);
System.out.println(shape.area()); // 100
shape = new Circle(10);
System.out.println(shape.area()); // 314.1592653589793
// ShowArea処理を使う
shape = new Rectangle(10, 10);
System.out.println(ShowArea.showArea(shape)); // 100.0
}
}
JavaScriptでも書いてみましょう。
糖衣構文って、やっぱりすごいですね。
まとめ
以上で、「インターフェイスの概念」をお送りしました。
インターフェイス(風な記述)をJavaScriptで書くと、どこがよくないのかという考察は、別途行いたいと思います。
次回は、「インターフェイスとアプリケーション設計」 をお届けします。