Python вопрос дизайна: когда зависимости не могут быть созданы раньше времени - PullRequest
0 голосов
/ 07 августа 2020

Извините за плохой заголовок, но если бы я знал класс проблем, над которыми я работал, возможно, я мог бы найти ответ.

В идеальной ситуации создается объект со всеми необходимыми зависимостями, а затем используется предмет. Однако в некоторых случаях информация, необходимая для создания зависимости, не известна до того, как объект будет использован.

Я попытаюсь создать конкретный пример, связанный с постройкой дома. Ниже перечислены зависимости

class WallMakerAlgorithm(abc.ABC):
    def build(self):
        # Put specific algorithm here that will build the wall
        pass

class DoorMakerAlgorithm(abc.ABC):
    def __init__(self, color: float, material: str):
        # subclasses can override init to add more fancy stuff
        self.color = color
        self.material = material

    def build(self):
        # Put specific algorithm here that will build the door
        pass

Они используются классом строителя дома. В идеальном случае HouseBuilder будет знать свои зависимости от init:

class BasicHouseBuilder:
    def __init__(self, door_maker_algorithm: DoorMakerAlgorithm, wall_maker_algorithm: WallMakerAlgorithm):
        self.door_maker_algorithm = door_maker_algorithm
        self.wall_maker_algorithm = wall_maker_algorithm

    def build(self):
        self.wall_maker_algorithm.build()
        self.door_maker_algorithm.build()

Но теперь предположим, что по какой-то (надуманной) причине заказчик не определился с цветом дверей. Они говорят вам, что как только стена будет построена, они будут знать цвет, потому что они смогут его видеть. Это создает проблему типа курицы и яйца. Я не могу создать HouseBuilder, потому что я не могу создать алгоритм создания двери, и я не могу создать алгоритм создания двери, не запустив на самом деле HouseBuilder. Примерно так:

class DecideColorLaterHouseBuilder:
   def __self__(self, wall_maker_algorithm: WallMakerAlgorithm, query_color: Callbale, ???):
      self.wall_maker_algorithm = wall_maker_algorithm
      self.query_color = query_color
      ???

   def build(self):
      self.wall_maker_algorithm.build()
      desired_door_color = self.query_color()
      # Now I want to build the door. But I don't have the door algorithm because I don't know the color!

Есть ли какой-нибудь шаблон проектирования, который решает такие проблемы? Мои абстракции вообще испорчены? Я потенциально могу добавить color в качестве аргумента к build из DoorMakingAlgorithm, но это просто испортит мои абстракции.

1 Ответ

0 голосов
/ 07 августа 2020

Вы можете использовать значения по умолчанию для аргументов ваших функций.

class DoorMakerAlgorithm(abc.ABC):
    def __init__(self, material: str, color: float=0xffffff):
        # arguments with default values (optional arguments) must come after mandatory ones
        # Here I put white color, but you could put any other color, or even None
        # subclasses can override init to add more fancy stuff
        self.color = color
        self.material = material

Использование None в качестве значения по умолчанию может позволить вам пройти через исключение, если пользователь все еще не определил цвет, когда вы build

class DoorMakerAlgorithm(abc.ABC):
    def __init__(self, material: str, color: float=None):
        self.color = color
        self.material = material

    def build(self):
        if self.color is None:
            raise Exception(f"Color hasn't been defined yet for {self}")
        # Then proceed to build the wall

Вы также можете использовать свойство, чтобы пользователь мог изменить цвет после создания экземпляра, пока вы все еще проверяете ввод. Обратите внимание, что в python пользователь всегда может получить доступ к любому атрибуту класса и изменить его. Свойство позволяет защитить ввод, но пользователь все равно может изменить атрибут _color и поместить туда что угодно. _ - это соглашение об атрибутах, с которыми конечный пользователь не должен связываться.

class DoorMakerAlgorithm(abc.ABC):
    def __init__(self, material: str, color: float=None):
        self.color = color
        self.material = material

    # You can use a property to allow the user to change the color later, and ensure input is correct
    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, color: float=None):
        self._color = color

    def build(self):
        if self.color is None:
            raise Exception(f"Color hasn't been defined yet for {self}")
        # Then proceed to build the wall

algo = DoorMakerAlgorithm("bricks")  # not giving a color
# algo.build()  # would through an exception
algo.color = 0xffffff
algo.build()  # works

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...