1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Python】サイコロクラスはどのように実装するのが最適か

Posted at

読者のあなたならどのような実装を行うかイメージしながら読んでみてほしい。

条件
『サイコロを回すクラス』から利用されることを念頭に置く。
このサイコロクラスは、サイコロを渡されると、ランダムに値を選択する処理になっている。


サイコロクラスはどのように実装を拡張するのが良いか
6面,8面サイコロ...というように作成していくとする。

サイコロを回すクラスの実装例(サイコロクラスは仕様のみ定義)
import random
import abc


class Dice(abc.ABC):
    """
    サイコロの仕様
    """

    __slots__ = ()  # インスタンスの属性追加を不可にする。

    @property
    @abc.abstractmethod
    def numbers(self) -> set:
        ...

class DiceRoller:
    """
    サイコロを回す役割
    ランダムに出目を選択する。
    """
    def __init__(self, dice: Dice) -> None:
        self._dice: Dice = dice

    def roll_dice(self) -> int:
        """
        ランダムに選択する。
        """
        return random.choice(list(self._dice.numbers))

実装例1 面の数ごとに、愚直に具象クラスを作成する。

抽象クラスの仕様を満たしたサイコロクラスを実装。共通化は行われていない。
この場合、抽象クラスはJavaで言うインターフェイスに近い。

class SixSidedDice(Dice):
    """
    6面ダイス
    """
    __slots__ = "__numbers"

    def __init__(self) -> None:
        self.__numbers = {1, 2, 3, 4, 5, 6}

    @property
    def numbers(self) -> set:
        return self.__numbers


class EightSidedDice(Dice):
    """
    8面ダイス
    """
    __slots__ = "__numbers"

    def __init__(self) -> None:
        self.__numbers = {1, 2, 3, 4, 5, 6, 7, 8}

    @property
    def numbers(self) -> set:
        return self.__numbers

実装例2 面の数ごとに、テンプレートパターンで共通化しつつ,具象クラスを作成する。

今回の場合、プロパティの実装なんて変わることがないと判断し、
抽象クラスにプロパティの実装をする場合

class Dice(abc.ABC):
    """
    サイコロの仕様
    """

    __slots__ = "_numbers"  # インスタンスの属性追加を不可にする。

    def __init__(self) -> None:
        self._numbers = None

    @property
    def numbers(self) -> set:
        if self._numbers is None:
            raise NotImplementedError('NotImplementedError')
        return self._numbers


class SixSidedDice(Dice):
    """
    6面ダイス
    """
    __slots__ = "_numbers"

    def __init__(self) -> None:
        super().__init__()
        self._numbers = {1, 2, 3, 4, 5, 6}


class EightSidedDice(Dice):
    """
    8面ダイス
    """
    __slots__ = "_numbers"

    def __init__(self) -> None:
        super().__init__()
        self._numbers = {1, 2, 3, 4, 5, 6, 7, 8}

実装例3 抽象クラスさえ不要と判断し、コンストラクタで切り替える手法を取る。

サイコロの面の数を変えた程度でクラスを別々に作ること自体が愚かだと
抽象クラスをやめて、インスタンス生成時にセットすればよいと判断した場合

class Dice:
    """
    サイコロの仕様
    """

    __slots__ = "_numbers"  # インスタンスの属性追加を不可にする。

    def __init__(self, numbers: set) -> None:
        self._numbers = numbers

    @property
    def numbers(self) -> set:
        return self._numbers

筆者の見解

サイコロの面の数の仕様程度で、具象クラスに分けるのは、少しやりすぎかもしれない。今回の場合であれば3を筆者は選ぶ。
ただし、抽象クラス(インターフェイス)で仕様を定めて、具象クラスに実装処理を記載するコーディング手法が強力であることには変わりはない

自身が執筆している
https://zenn.dev/timoneko/books/78460a539d033f
OOPの解説のために、少し仰々しく実装した実装例1を載せているが....

1
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?