Что является более питоническим, фабричным как функция в модуле или как метод в классе, который он создает? - PullRequest
3 голосов
/ 22 сентября 2008

У меня есть некоторый код Python, который создает объект Calendar на основе проанализированных объектов VEvent и файла iCalendar.

У объекта календаря есть метод, который добавляет события при их анализе.

Теперь я хочу создать фабричную функцию, которая создает календарь из файлового объекта, пути или URL.

Я использовал модуль Python iCalendar , который реализует функцию фабрики как метод класса непосредственно в классе, для которого он возвращает экземпляр:

cal = icalendar.Calendar.from_string(data)

Из того, что я мало знаю о Java, это общий шаблон в коде Java, хотя я, похоже, нахожу больше ссылок на метод фабрики, находящийся в другом классе, чем класс, из которого вы на самом деле хотите создавать экземпляры.

Вопрос в том, считается ли это также питонским? Или считается более питонным просто создавать метод уровня модуля как фабричную функцию?

Ответы [ 5 ]

12 голосов
/ 22 сентября 2008

[ Примечание . Будьте очень осторожны при разделении «Календаря» на собрание событий, а «Событие» - на одно событие в календаре. В вашем вопросе кажется, что может быть некоторая путаница.]

Существует множество вариантов шаблона проектирования фабрики.

  1. Автономная удобная функция (например, CalendarMaker (данные))

  2. Отдельный класс (например, CalendarParser), который создает целевой класс (Календарь).

  3. Метод уровня класса (например, Calendar.from_string).

У них разные цели. Все питоники, вопросы такие: "что вы имеете в виду ?" и "что может измениться?" Смысл это все; изменение важно.

Удобные функции - Pythonic. Такие языки, как Java, не могут иметь свободно плавающих функций; Вы должны обернуть одинокую функцию в классе. Python позволяет вам иметь одиночную функцию без издержек класса. Функция уместна, когда ваш конструктор не имеет изменений состояния или альтернативных стратегий или какой-либо памяти о предыдущих действиях.

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

Используется шаблон метода уровня класса, но он имеет ограничения. Во-первых, он вынужден полагаться на переменные уровня класса. Поскольку это может сбивать с толку, сложный конструктор как статический метод сталкивается с проблемами, когда вам необходимо добавить функции (например, отслеживание состояния или альтернативные стратегии). Убедитесь, что вы никогда не собираетесь расширять статический метод.

Во-вторых, он более или менее не имеет отношения к остальным методам и атрибутам класса. Этот вид from_string является лишь одним из многих альтернативных кодировок для ваших объектов Календаря. Возможно, у вас есть from_xml, from_JSON, from_YAML и так далее. Ничто из этого не имеет ни малейшего отношения к тому, что такое Календарь или что он делает. Эти методы все о том, как Календарь кодируется для передачи.

В зрелых библиотеках Python вы увидите, что фабрики отделены от того, что они создают. Кодирование (в виде строк, XML, JSON, YAML) подвержено большому или меньшему количеству случайных изменений. Однако существенная вещь редко меняется.

Разделите две проблемы. Держите кодировку и представление как можно дальше от состояния и поведения.

6 голосов
/ 22 сентября 2008

Пифонично не думать об эзотерическом различии в каком-то шаблоне, который вы где-то читали и теперь хотите использовать везде, например, в заводском шаблоне.

Большую часть времени вы бы думали о @staticmethod как о решении, вероятно, лучше использовать функцию модуля, за исключением случаев, когда вы объединяете несколько классов в одном модуле, и каждый имеет разную реализацию одного и того же интерфейса, тогда лучше используйте @ staticmethod

В конечном итоге, когда вы создаете свои экземпляры с помощью @staticmethod или с помощью функции модуля, разница невелика.

Я бы, вероятно, использовал инициализатор (__init__) класса, потому что одним из наиболее приемлемых "шаблонов" в python является то, что фабрика для класса - это инициализация класса.

2 голосов
/ 22 сентября 2008

ИМХО метод уровня модуля - более чистое решение. Он скрывается за системой модулей Python, которая дает ему уникальный префикс пространства имен, для которого обычно используется «фабричный шаблон».

0 голосов
/ 22 сентября 2008

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

Фабричная функция в модуле всегда создает экземпляр типа 'right' (где 'right' в вашем случае - это всегда класс 'Calendar', но вы также можете сделать его зависимым от содержимого того, чем оно является создание экземпляра из.)

Используйте метод класса, если вы хотите, чтобы он зависел не от данных, а от класса, к которому вы обращаетесь. Метод класса подобен статическому методу в том, что вы можете вызывать его в классе без экземпляра, но он получает класс, в который он был вызван, в качестве первого аргумента. Это позволяет вам фактически создать экземпляр этого класса , который может быть подклассом исходного класса. Примером метода класса является dict.fromkeys (), который создает dict из списка ключей и одного значения (по умолчанию None.) Поскольку это метод класса, когда вы используете подкласс dict, вы получаете метод 'fromkeys' совершенно бесплатно , Вот пример того, как можно написать dict.fromkeys () самостоятельно:

class dict_with_fromkeys(dict):
    @classmethod
    def fromkeys(cls, keys, value=None):
        self = cls()
        for key in keys:
            self[key] = value
        return self
0 голосов
/ 22 сентября 2008

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

...