Проблема шаблона проектирования объектов в Python - PullRequest
1 голос
/ 06 октября 2009

Мне нужно принять следующее дизайнерское решение - столкнуться с ним во второй раз, поэтому я надеюсь, что я не единственный ...

У меня есть различные классы команд, которые представляют некоторые действия. Каждый класс Command (существует множество, скажем, SleepCommand, RunCommand, MeasureCommand) имеет разные параметры со значениями по умолчанию и т. Д., Но в конце каждый имеет метод generate(), который возвращает некоторое упакованное представление.

Теперь у меня также есть класс CommandSequence, который объединяет много команд в списке. У него есть собственный метод generate (), который выполняет некоторую пользовательскую работу, запускает generate() для всех команд в своем списке, добавляет свои собственные элементы и т. Д.

У меня есть сомнения относительно того, как создавать эти объекты команд. Есть несколько альтернатив:

(1) Позволяет пользователю создавать экземпляры различных объектов Command и просто вызывать метод add_command () для CommandSequence (2) Создайте «фабричный метод» в CommandSequence для каждой из возможных команд. Методы фабрики принимают аргументы команды и передают их конструктору команды. (3) Поскольку метод (2) дублирует код (списки инициализации конструктора объектов), я могу просто заставить все фабричные методы принимать *args, **kwargs и передавать их объектам.

Теперь подход (1) не дублирует код, но предоставляет пользователю множество классов Command. В отличие от (2) код пользователя более подробный:

# seq is a CommandSequence object
seq.add_command(SleepCommand(time='10 ms', wakeup_alarm=False))

вместо:

seq.sleep(time='10 ms', wakeup_alarm=False)

Поскольку я создаю своего рода DSL, краткость и ясность кода пользователя очень важны для меня. Я могу использовать метод (3), чтобы сэкономить на дублировании кода, но затем, где разместить документацию классов Command - в фабричных методах или в самих конструкторах классов.

Я что-то упустил здесь? Как бы вы подошли к этому?

Ответы [ 3 ]

3 голосов
/ 06 октября 2009

Я бы придерживался 1.

Когда у вас есть часть кода, необходимая для выполнения SleepCommand, для меня имеет смысл выставить интерфейс конструирования SleepCommand для этой части кода. Как еще они могут построить один?

Вы могли бы использовать фабрику, но я не думаю, что она принесет вам большую пользу (кроме дублирования кода, как вы указали). Это также означает, что каждый раз, когда вам нужно добавить новую команду, вам нужно перейти и изменить класс CommandSequence, чтобы предоставить интерфейс для выполнения новой команды. Это нарушает принцип открытого-закрытого (http://en.wikipedia.org/wiki/Open/closed_principle).

Редактировать: еще один важный момент заключается в том, что номер 1 является более простой дизайн. Чтобы добавить новую команду, просто создайте новый класс команд - это хороший ОО-дизайн. Кроме того, с двумя строками кода, которые вы пишете в вопросе, я не думаю, что вторая действительно намного сложнее первой.

1 голос
/ 06 октября 2009

Еще одна опция,

Шаг 1. Создайте конструктор CommandSequence, который принимает несколько объектов команды через списки переменных аргументов

Шаг 2. Создайте статическую константу (я не прога Python, поэтому, пожалуйста, простите, если это невозможно) объекты типа CommandSequence "inside" CommandSequence, инициализированные командами для часто используемых последовательностей команд.

Это поможет клиентам в написании кода типа

CommandSequence.XYZCommandSeq.generate

Помогает избежать дублирования во многих местах.

0 голосов
/ 06 октября 2009

Я бы избегал (3). Вы теряете документацию ... поэтому вам придется что-то дублировать ...

В (1) вы упоминаете множество объектов, которые должен знать вызывающий разработчик. Но именно эти объекты и должны знать вызывающий разработчик, поэтому это кажется необходимой сложностью.

Для (2), как вы говорите, это дублирование.
Кроме того, для каждой новой команды вам придется поддерживать ваши фабричные методы ...

...