018 オブジェクト指向の深化(インターフェイスと抽象クラスの利用) 001 解答例

// Shape クラス(抽象クラス)
abstract class Shape {
    // 面積を計算して返す抽象メソッド
    abstract double getArea();

    // 周囲の長さを計算して返す抽象メソッド
    abstract double getPerimeter();
}

// Circle クラス(円)
class Circle extends Shape {
    // メンバー変数:半径
    private double radius;

    // コンストラクタ
    public Circle(double radius) {
        this.radius = radius;
    }

    // 面積を計算して返すメソッド
    @Override
    double getArea() {
        return Math.PI * radius * radius;
    }

    // 周囲の長さを計算して返すメソッド
    @Override
    double getPerimeter() {
        return 2 * Math.PI * radius;
    }
}

// Rectangle クラス(長方形)
class Rectangle extends Shape {
    // メンバー変数:幅、高さ
    private double width;
    private double height;

    // コンストラクタ
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    // 面積を計算して返すメソッド
    @Override
    double getArea() {
        return width * height;
    }

    // 周囲の長さを計算して返すメソッド
    @Override
    double getPerimeter() {
        return 2 * (width + height);
    }
}

// Triangle クラス(三角形)
class Triangle extends Shape {
    // メンバー変数:辺の長さ(3辺)
    private double side1;
    private double side2;
    private double side3;

    // コンストラクタ
    public Triangle(double side1, double side2, double side3) {
        this.side1 = side1;
        this.side2 = side2;
        this.side3 = side3;
    }

    // 面積を計算して返すメソッド(※実際の三角形の面積計算は省略)
    @Override
    double getArea() {
        // 面積を計算する具体的な処理を実装
        // ここでは単純に 0 を返していますが、実際には三角形の面積計算を実装する必要があります。
        return 0;
    }

    // 周囲の長さを計算して返すメソッド
    @Override
    double getPerimeter() {
        return side1 + side2 + side3;
    }
}

この解答例では、Shape クラスが抽象メソッド getArea()getPerimeter() を定義し、CircleRectangleTriangle クラスがそれぞれこれらのメソッドをオーバーライドしています。これにより、異なる形状のオブジェクトが共通のインターフェースを提供するクラス階層が構築されています。なお、Triangle クラスの getArea() メソッドは実際の三角形の面積計算を行うように実装されるべきですが、ここでは単純な例として 0 を返しています。

インターフェイスと抽象クラスの利用

インターフェイスと抽象クラスの役割

抽象クラス:

役割:
共通の振る舞いや構造を持つクラスを定義するための基盤となります。
抽象メソッドや具象メソッドを含むことができ、一部のメソッドをサブクラスで実装することが求められることがあります。

利点:
共通のメソッドやメンバー変数を提供でき、サブクラスがこれらを継承して利用することができます。
具象メソッドを含めることができ、サブクラスが共通の振る舞いを再利用する際に便利です。

abstract class AbstractShape {
    abstract double getArea();
    abstract double getPerimeter();
}

インターフェイス:

役割:
実装されているクラスが特定のメソッドを提供することを保証するための契約を定義します。
複数のインターフェイスを実装することができます。

利点:
クラスが複数のインターフェイスを実装することで、異なるコンセプトを組み合わせた柔軟な設計が可能です。
インターフェイスを実装するクラスは、そのインターフェイスが要求するメソッドを提供する必要があります。

interface Shape {
    double getArea();
    double getPerimeter();
}

インターフェイスと抽象クラスの選択基準

抽象クラスを選ぶ場合:

共通のコードやデータを提供する必要がある場合。
サブクラスによる拡張が必要であり、一部のメソッドを既に具象化することができる場合。

abstract class AbstractShape {
    double getArea() {
        // 共通の実装
        return 0;
    }
}

インターフェイスを選ぶ場合:

複数のクラスによって共有される振る舞いを指定する場合。
既に他のクラスを拡張している場合や、複数のインターフェイスを実装する柔軟性が必要な場合。

interface Shape {
    double getArea();
}

共通の利用シナリオ

複数のインターフェイスを実装:

クラスが複数の異なる役割を果たす必要がある場合、複数のインターフェイスを実装することができます。

class Circle implements Shape, Drawable {
    // ...
}

抽象クラスを基底クラスとして利用:

共通の振る舞いを提供するために抽象クラスを定義し、サブクラスがこれを拡張して利用します。

abstract class AbstractShape {
    // ...
}

class Circle extends AbstractShape {
    // ...
}

まとめ

インターフェイスと抽象クラスは、オブジェクト指向プログラミングの設計において異なる目的で使用されます。抽象クラスは共通の実装を提供し、インターフェイスは共通の振る舞いを指定します。これらを組み合わせて柔軟なオブジェクトの設計を行うことで、効果的かつメンテナンスしやすいコードを実現することができます。

「018 オブジェクト指向の深化」問題集リスト