Избавление от операторов if при создании динамических подтипов - PullRequest
0 голосов
/ 02 марта 2019

Вопрос в основном о дизайне.

Предположим, у нас есть класс, который играет роль инициации различных подклассов определенного типа по некоторым параметрам, через которые он итерирует.Проблема возникает, когда метод __init__ получает разные параметры для каждого подтипа.Есть ли способ избежать операторов if внутри функции, которая инициализирует классы, просто чтобы знать, какие параметры передать?Может быть, какой-то шаблон дизайна, о котором я не знаю.Или это результат плохого дизайна?

ниже - пример того, что я имею в виду.обратите внимание на статический метод управления, в котором есть if ... else ... и если бы было больше типов работников, у нас было бы больше if, что я и стараюсь избегать.Имейте в виду, что пример минимален, и операторы if могут быть гораздо более сложными.

from abc import ABCMeta


class BaseWorker(metaclass=ABCMeta):
    def work(self):
        pass


class Worker1(BaseWorker):
    def __init__(self, name):
        self.name = name

    def work(self):
        pass


class Worker2(BaseWorker):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def work(self):
        pass


class Manager(object):
    @staticmethod
    def manage(attributes_list):
        for attributes in attributes_list:
            if "age" in attributes:
                w = Worker2(name=attributes["name"], age=attributes["age"])
            else:
                w = Worker1(name=attributes["name"])
            w.work()


if __name__ == '__main__':
    dynamic_attributes = [
        {"name": "davay"},
        {"age": "55", "name": "ok"},
        # and so on...
    ]
    Manager.manage(dynamic_attributes)

И желаемое решение было бы

    @staticmethod
    def desired_manage(attributes_list):
        for attributes in attributes_list:
            w = worker_factory(attributes)
            w.work()

** Обратите внимание, что worker_factory - это просто произвольное название способа решения этой проблемы, это не означает, что фабричный шаблон - это путь.Даже меньше, если мы попробуем фабричный шаблон, из того, что я вижу, операторы if просто переместятся туда и ничего не решат.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 02 марта 2019

Один из способов сделать это состоит в том, чтобы зарегистрировать каждый подкласс в менеджере вместе с вызываемым элементом, который определяет, следует ли создавать экземпляр подкласса.

Примерно так (непроверено):

class Manager(object):

    _registry = []
    _default = None

    @classmethod
    def register(cls, callable, klass):
        cls._registry.append((callable, klass))
        return

    @classmethod
    def register_default(cls, klass):
        cls._default = klass
        return

    @staticmethod
    def manage(attributes_list):
        for attributes in attributes_list:
            for callable, klass in Manager._registry:
                if callable(attributes):
                    w = klass(**attributes)
                    break
        else:
            w = Manager._default(**attrs)
        w.work()


class BaseWorker(metaclass=ABCMeta):
    def work(self):
        pass


class Worker1(BaseWorker):
    def __init__(self, name):
        self.name = name

Manager.register_default(Worker1)  


class Worker2(BaseWorker):
    def __init__(self, name, age):
        self.name = name
        self.age = age 

Manager.register(Worker2, lambda attrs: 'age' in attrs)

Этот метод имеет некоторые недостатки:

  • Условия проверяются в порядке вставки, поэтому необходимо убедиться, что более строгие условия, например 'name' in attrs and 'age' in attrs, проверены перед более слабыми условиями, такими как 'name' in attrs.Это может быть сложно, если подклассы определены в разных модулях.Возможно, добавьте приоритет к каждому (callable, klass) кортежу, который можно использовать для сортировки реестра
  • Вложенный цикл может быть медленным, если attributes_list велико (и / или имеется много подклассов)
0 голосов
/ 02 марта 2019

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

from abc import ABCMeta


class BaseWorker(metaclass=ABCMeta):
    def work(self):
        pass


class Worker(BaseWorker):
    def __init__(self, name, age=None, address=None, sex=None):
        self.name = name
        self.age = age
        address= address
        sex= sex

    def work(self):
        pass


class Manager(object):
    @staticmethod
    def manage(attributes_list):
        for attributes in attributes_list:
            w = Worker(**attributes)
            w.work()


if __name__ == '__main__':
    dynamic_attributes = [
        {"name": "davay"},
        {"name": "aa", "age": "55"},
        {"name": "bb", "sex": "female"},
        {"name": "cc", "address": "123 street", "age": "43"},
        # and so on...
    ]
    Manager.manage(dynamic_attributes)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...