😆

単一継承しかサポートしていない言語で多重継承したくなった時の解決策

に公開

多くのオブジェクト指向言語(Java, C#, Swiftなど)はクラスの単一継承のみをサポートしています。
しかし、実際の開発では複数のクラスの機能をまとめて使いたい」と感じる場面が少なくありません。

そこで本記事では、単一継承しかできない言語で多重継承のような構造を実現するための代表的な手法をまとめたいと思います。

1. インターフェース (Interface)を使う

最も一般的な方法は、インターフェースを使って型の契約だけを複数継承することです。

Javaの例

interface Flyable {
    void fly();
}

interface Runnable {
    void run();
}

class Robot implements Flyable, Runnable {
    @override
    public void fly() {
        System.out.println("Robot is flying");
    }

    @override
    public void run() {
        System.out.println("Robot is running");
    }
}

interfaceは複数実装することができますので、「複数の能力を持つ」ことを表現するには最適です。

2. 委譲 (Delegation)を使う

多重継承の代わりに、別クラスのインスタンスを内部に持ち、その機能を委譲する方法です。つまり、処理の中身は内部で使っているオブジェクトの方に書いといてもらう。

Javaの例

class Engine {
    void start() {
        System.out.println("Engine starts");
    }
}

class Radio {
    void play() {
        System.out.println("Radio plays music");
    }
}

class Car {
    private Engine engine = new Engine();
    private Radio radio = new Radio();

    void startEngine() {
        engine.start(); // Deletgation
    }

    void playRadio() {
        radio.play(); // Delegation
    }
}

委譲を使うことで「CarがEngineとRadioの機能を持つ」という多重継承的な構造を安全に実現できます。

3. ミックスイン (Mixin)風の実装

言語によっては、ミックスイン風の構造をサポートしている場合があります(Ruby, Python, JSなど)。
ミックスインとはクラスに別のクラス(オブジェクト)の機能を混ぜ込む仕組みで、特定のメソッド群だけをクラスに追加するという考え方です。

JavaScriptの例 (mixin)

const Flyable = {
    fly() {
        console.log("Flying!");
    }
};

const Runnable = {
    run() {
        console.log("Running!");
    }
};

function mixin(target, ...sources) {
    Object.assign(target.prototype, ...sources);
}

class Robot {}
mixin(Robot, Flyable, Runnable);

const r = new Robot();
r.fly();
r.run();

JavaScriptのような柔軟な言語では、複数の機能をクラスに混ぜ込むことができます。

4. トレイト (Trait)を使う

PHPやRustなどでは、トレイトという仕組みがあり、多重継承の問題を解決するために設計されています。

PHPの例

trait Logger {
    public function log($msg) {
        echo "[LOG] $msg\n";
    }
}

trait Notifier {
    public function notify($msg) {
        echo "[NOTIFY] $msg\n";
    }
}

class UserService {
    use Logger, Notifier;
}

$service = new UserService();
$service->log("Start");
$service->notify("Done");

トレイトは「コードの再利用」を目的としており、多重継承の欠点(ダイアモンド問題)を避けつつ、複数の機能を安全に取り込めます。

5. コンポジション (Composition)を基本にする

オブジェクト指向設計の原則として有名な言葉に

「継承より合成を優先せよ (Favor composition over inheritance)」

というものがあります。

コンポジションとは

コンポジション (Composition)とは、「オブジェクトを部品として持つ設計」のことです。

Javaの例

class Engine {
    void start() {
        System.out.println("Engine start");
    }
}

class Car {
    private Engine engine = new Engine(); // Conposition

    void start() {
        engine.start(); // ここはDelegation
    }
}

# まとめ
単一系少子化サポートしていない言語も、以下のような手法を使うことで多重継承に近い構造を実現できます。
|手法|特徴|
|---|---|
|interface|複数の契約を実装できる|
|delegation|実装を別クラスに任せることで柔軟性が高い|
|mixin|複数の機能をクラスに混ぜ込める|
|trait|元々の実装を用意しておいてuseする|
|Composition|別オブジェクトを部品として内部に持つ設計|

多重継承が必要に見える場面でも、これらの手法を組み合わせることで安全かつ柔軟な設計が可能となります。ß

Discussion