概要
Java, Python等のオブジェクト指向言語を扱っていると避けて通れないのがクラスという概念
今回は抽象クラスについてまとめていこうと思います
記事の対象者
- 抽象クラスを知らない人
- オブジェクト指向について勉強中の人
抽象クラスとは?
具体的な実装を持たないメソッドを一つ以上含むクラスのこと のようです
抽象クラスはIS Aの関係であるため親クラスでできることはもちろん子供クラスでもできます
抽象クラスが存在する意味
- 複数のクラスで共通してもつべきメソッドの「形」を定義することで、コードの統一性と保守性を高めます
- 子クラスは、抽象クラスの抽象メソッドを必ず実装しなければならないため、これにより、子クラスが最低限備えるべき機能を保証できる
抽象クラスのメリット
- 共通の機能を抽象クラスにまとめることで、コードの重複を減らし、保守性を向上できる
- システム全体の構造を明確にし、開発チーム間の共通理解を促進できる
- 抽象クラスを基底クラスとすることで、多様なオブジェクトを統一的に扱うことができる
実装例
図形の面積を計算するパターンで考えます
抽象クラス
抽象メソッドにはデコレータを指定します
calc_surface_areaの抽象メソッドをもつ、図形抽象クラスができました
shape.py
import abc
class Shapes(metaclass=abc.ABCMeta):
@abc.abstractmethod
def calc_surface_area(self) -> int:
# pass を使うよりもこちらのほうが妥当
raise NotImplementedError()
実装クラス
では、図形抽象クラスを継承する実装クラスを考えます
一番下に図形抽象クラスを継承していないが、同じメソッド名をもつDiamondクラスがありますが、これは次のユースケースで説明します
shape.py
import math
# 実装クラス
class Square(Shapes):
def __init__(self, width: int, height: int):
self.width = width
self.height = height
def calc_surface_area(self) -> int:
return self.width * self.height
# 実装クラス
class Circle(Shapes):
def __init__(self, radius: float):
self.radius = radius
def calc_surface_area(self) -> float:
return math.pi * self.radius * self.radius
class Diamond:
def __init__(self, width: float, height: float):
self.width = width
self.height = height
def calc_surface_area(self) -> float:
return self.width * self.height / 2
ユースケース
実際の現場では同じメソッド名をもつが特定の抽象クラスを継承している場合のみ、メソッドを実行したい場合があると思います
以下のように型を判定をするだけで、図形抽象クラスを継承しているsquareとcircleのみがcalc_surface_area()を実行することができます
main.py
from shape import Shapes, Circle, Square, Diamond
if __name__ == '__main__':
square = Square(width=5, height=5)
circle = Circle(radius=12)
diamond = Diamond(width=10, height=5)
shapes = [square, circle, diamond]
for shape in shapes:
# 型がShapesであるかどうかだけを判断している -> 分岐を削減することができる
if isinstance(shape, Shapes):
print(shape.calc_surface_area())
else:
# diamondはShapesを継承するクラスではないので、elseに入る
print(shape.__class__.__name__)