27.

ポリモーフィズム (多態性) の具体例 - 動物クラス・図形・Strategy パターン・実務での効果

編集
この記事の要点
  • ポリモーフィズム: 同じメソッド呼び出しでも、実体クラスに応じて挙動が変わる仕組み
  • 代表例: Animal 型の参照で Dog.bark()Cat.bark() を呼ぶと、それぞれの実装が動く
  • instanceof での分岐が不要になる → 拡張に強い (OCP: 開放閉鎖原則)
  • List を for で回すだけで各動物が自分の動きをする
  • 実務での効果: Strategy パターン / Template Method / 戦略の切り替えがコード変更なしで可能

ポリモーフィズムとは

「多態性」「多様性」と訳される OOP の中心概念。同じ操作 (メソッド呼び出し) でも、対象の型によって異なる挙動になる性質。Java / PHP / Python など多くの言語で「オーバーライド」+「上位型での参照」で実現する。

古典例: 動物クラス

// 親クラス (抽象)
public abstract class Animal {
    private String name;
    public Animal(String name) { this.name = name; }
    public String getName() { return name; }

    public abstract String bark();   // サブクラスでオーバーライド
}

// 犬
public class Dog extends Animal {
    public Dog(String name) { super(name); }
    @Override
    public String bark() { return "ワン!"; }
}

// 猫
public class Cat extends Animal {
    public Cat(String name) { super(name); }
    @Override
    public String bark() { return "ニャー"; }
}

// 牛
public class Cow extends Animal {
    public Cow(String name) { super(name); }
    @Override
    public String bark() { return "モー"; }
}

ポリモーフィズムが効くシーン

// ✅ Animal 型のリストに犬・猫・牛を混在させる
List animals = new ArrayList<>();
animals.add(new Dog("ポチ"));
animals.add(new Cat("タマ"));
animals.add(new Cow("モモ"));

// 同じループで各々の鳴き声が出る
for (Animal a : animals) {
    System.out.println(a.getName() + ": " + a.bark());
}
// → ポチ: ワン!
//   タマ: ニャー
//   モモ: モー

// 新しい動物 (Sheep) を追加してもループは無修正!
animals.add(new Sheep("ふわ"));   // → ふわ: メェー

ポリモーフィズムが無い世界 (反面教師)

// ❌ ポリモーフィズム未使用: instanceof で分岐
public String bark(Object animal) {
    if (animal instanceof Dog) {
        return "ワン!";
    } else if (animal instanceof Cat) {
        return "ニャー";
    } else if (animal instanceof Cow) {
        return "モー";
    }
    // 動物が増えるたびに if が増える → OCP 違反
    return "...";
}

// ✅ ポリモーフィズム使用: 呼ぶだけ
public String bark(Animal animal) {
    return animal.bark();   // 1 行で済む
}

図形クラスでの例

public abstract class Shape {
    public abstract double area();
    public abstract double perimeter();
}

public class Circle extends Shape {
    private double radius;
    public Circle(double r) { this.radius = r; }
    public double area() { return Math.PI * radius * radius; }
    public double perimeter() { return 2 * Math.PI * radius; }
}

public class Rectangle extends Shape {
    private double width, height;
    public Rectangle(double w, double h) { this.width = w; this.height = h; }
    public double area() { return width * height; }
    public double perimeter() { return 2 * (width + height); }
}

public class Triangle extends Shape {
    private double a, b, c;
    public Triangle(double a, double b, double c) { this.a=a; this.b=b; this.c=c; }
    public double area() {
        double s = (a + b + c) / 2.0;
        return Math.sqrt(s * (s-a) * (s-b) * (s-c));
    }
    public double perimeter() { return a + b + c; }
}

// 利用
List shapes = List.of(
    new Circle(5),
    new Rectangle(3, 4),
    new Triangle(3, 4, 5)
);

double totalArea = shapes.stream()
    .mapToDouble(Shape::area)
    .sum();

ファクトリパターンとの組み合わせ

// 文字列から動物を生成
public class AnimalFactory {
    public static Animal create(String kind, String name) {
        return switch (kind) {
            case "dog" -> new Dog(name);
            case "cat" -> new Cat(name);
            case "cow" -> new Cow(name);
            default    -> throw new IllegalArgumentException(kind);
        };
    }
}

// 利用
String[] kinds = { "dog", "cat", "cow" };
List animals = Arrays.stream(kinds)
    .map(k -> AnimalFactory.create(k, "?"))
    .toList();

animals.forEach(a -> System.out.println(a.bark()));

Strategy パターン (戦略パターン)

「アルゴリズムを差し替え可能にする」古典的なポリモーフィズム応用:

// 並び替え戦略
public interface SortStrategy {
    List sort(List list);
}

public class BubbleSort> implements SortStrategy {
    public List sort(List list) { /* O(n^2) */ return ...; }
}
public class QuickSort> implements SortStrategy {
    public List sort(List list) { /* O(n log n) */ return ...; }
}
public class MergeSort> implements SortStrategy {
    public List sort(List list) { /* 安定 */ return ...; }
}

// 利用側はインターフェースだけ知っている
public class Sorter {
    private SortStrategy strategy;
    public Sorter(SortStrategy s) { this.strategy = s; }
    public List doSort(List list) { return strategy.sort(list); }
}

new Sorter<>(new QuickSort<>()).doSort(myList);
new Sorter<>(new MergeSort<>()).doSort(myList);

Template Method パターン

共通の流れだけ親クラスに書き、差分はサブクラスで上書き:

public abstract class ReportGenerator {
    // テンプレートメソッド (確定アルゴリズム)
    public final String generate() {
        StringBuilder sb = new StringBuilder();
        sb.append(header());
        sb.append(body());      // ← サブクラスで実装
        sb.append(footer());
        return sb.toString();
    }
    protected String header() { return "===== レポート =====\n"; }
    protected String footer() { return "==== ここまで ====\n"; }
    protected abstract String body();
}

public class SalesReport extends ReportGenerator {
    protected String body() { return "売上: 1,000,000円"; }
}
public class StockReport extends ReportGenerator {
    protected String body() { return "在庫: 250 個"; }
}

new SalesReport().generate();
new StockReport().generate();

実務での効果

シーンポリモーフィズム適用例
支払い方法カード / 銀行 / コンビニ → PaymentMethod#pay()
通知Email / Slack / Push → Notifier#send()
エクスポートCSV / Excel / PDF → Exporter#export()
認証Local / OAuth / SAML → Authenticator#authenticate()
キャッシュRedis / Memcached / File → Cache#get()

PHP での例

send($to, $msg);
    }
}

notifyAll([
    new EmailNotifier(),
    new SlackNotifier(),
    new LineNotifier(),
], 'user@example.com', '緊急通知');

FAQ

Q: オーバーロード (引数違いの同名メソッド) もポリモーフィズム?
A: 厳密にはアドホック多相と呼ばれ別物。OOP で「ポリモーフィズム」と言えば通常はオーバーライドによるサブタイプ多相を指す。

Q: ポリモーフィズムを使うとパフォーマンスが落ちる?
A: 仮想メソッド呼び出しのコストは現代の JIT で誤差レベル。可読性・保守性のメリットが圧倒的に勝る。

Q: 関数型言語のポリモーフィズム?
A: パラメトリック多相 (ジェネリクス) + 型クラス (Haskell) / 高階関数で実現。OOP のサブタイプ多相とは別系統。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本事項
  2. HTMLへの埋め込み
  3. 変数
  4. 可変変数
  5. 定数
  6. データ型
  7. キャスト
  8. エスケープ文字
  9. 配列
  10. 演算子
  11. 代入の際の注意点
  12. 条件分岐
  13. 繰り返し処理
  14. クラスとインスタンス
  15. コンストラクタ
  16. 関数
  17. スーパーグローバル変数
  18. スコープ
  19. staticについて
  20. yieldについて
  21. ファイルのアップロード方法
  22. DB接続方法
  23. SQL実行方法
  24. カプセル化の具体例
  25. 継承の構文
  26. オーバーライド
  27. ポリモーフィズム(多様性)の具体例
  28. 抽象クラス・メソッドの構文と具体例
  29. GET通信
  30. try catchで全てのエラーを拾う方法