Идиома или шаблон дизайна для шаблона класса? - PullRequest
1 голос
/ 16 января 2012

Моя кодовая база на Python. Допустим, у меня есть довольно общий класс под названием Report. Требуется большое количество параметров

class Report(object):
  def __init__(self, title, data_source, columns, format, ...many more...)

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

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

Если подкатегория отчета нуждается в дополнительном коде обработки, подкласс является хорошим выбором. Скажем, у нас есть подкатегория ExpenseReport.

class ExpenseReport(Report):
    def __init__(self, title, ... a small number of parameters ...)

        # some parameters are fixed, while others are specific to this instance
        super(ExpenseReport,self).__init__(
                title,
                EXPENSE_DATA_SOURCE,
                EXPENSE_COLUMNS,
                EXPENSE_FORMAT,
                ... a small number of parameters...)

    def processing(self):
        ... extra processing specific to ExpenseReport ...

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

ExpenseReport = functools.partial(Report,
                        data_source = EXPENSE_DATA_SOURCE,
                        columns = EXPENSE_COLUMNS,
                        format = EXPENSE_FORMAT,
                )

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

expense_report = Report("Total Expense", EXPENSE_DATA_SOURCE, ...)
page1.add(expense_report)

...
page2.add(clone(expense_report))

И в моей кодовой базе используется уродливая техника. Поскольку нам нужно 2 отдельных экземпляра для каждой страницы и поскольку мы не хотим дублировать код с длинным списком параметров, создающих отчет, мы просто клонируем (глубокая копия в Python) отчет для страницы 2. Не только необходимость клонирование не очевидно, пренебрежение клонированием объекта и совместное использование одного экземпляра создает множество скрытых проблем и тонких ошибок в нашей системе.

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

Дополнительная информация

Мы используем параметр ключевого слова. Проблема скорее в том, как управлять и организовывать реализацию. У нас есть большое количество экземпляров с общими шаблонами:

expense_report = Report("Expense", data_source=EXPENSE, ..other common pattern..)
expense_report_usd = Report("USD Expense", data_source=EXPENSE, format=USD, ..other common pattern..)
expense_report_euro = Report("Euro Expense", data_source=EXPENSE, format=EURO, ..other common pattern..)
...
lot more reports 
...
page1.add(expense_report_usd)
page2.add(expense_report_usd)   # oops, page1 and page2 shared the same instance?!
...
lots of pages
...

Ответы [ 5 ]

2 голосов
/ 16 января 2012

Почему бы вам просто не использовать аргументы с ключевыми словами и собрать их все в dict:

class Report(object):
  def __init__(self, **params):
      self.params = params
      ...
0 голосов
/ 06 января 2018

Вопрос довольно старый, но он все еще может помочь кому-то, кто наткнется на него ...

Я создал небольшую библиотеку под названием classic , чтобы упростить случаи наследования классов, подобные этой (PythonТолько 3).

Простой пример:

from classical.descriptors import ArgumentedSubclass

class CustomReport(Report):
    Expense = ArgumentedSubclass(data_source=EXPENSE, **OTHER_EXPENSE_KWARGS)
    Usd = ArgumentedSubclass(format=USD)
    Euro = ArgumentedSubclass(format=EURO)
    PatternN = ArgumentedSubclass(**PATTERN_N_KWARGS)
    PatternM = ArgumentedSubclass(**PATTERN_M_KWARGS)

# Now you can chain these in any combination (and with additional arguments):
my_report_1 = CustomReport.Expense.Usd(**kwargs)
my_report_2 = CustomReport.Expense.Euro(**kwargs)
my_report_3 = CustomReport.Expense.PatternM.PatternN(**kwargs)

В этом примере нет необходимости разделять классы Report и CustomReport, но может быть хорошей идеей сохранить исходныйкласс "clean".

Надеюсь, это поможет:)

0 голосов
/ 18 января 2012

Я сам нашел некоторую информацию.

I. curry - связывание параметров с функцией «Python recipes« ActiveState Code

http://code.activestate.com/recipes/52549-curry-associating-parameters-with-a-function/

См. Все обсуждения. Комментарий Ника Перкинса к подклассам «Легкий вес» похож на то, что я описал.

II. PEP 309 - применение частичной функции

http://www.python.org/dev/peps/pep-0309/

0 голосов
/ 16 января 2012

Если ваша основная проблема - общие аргументы в конструкциях классов, возможное решение - написать что-то вроде:

common_arguments = dict(arg=value, another_arg=anoter_value, ...)

expense_report = Report("Expense", data_source=EXPENSE, **common_arguments)
args_for_shared_usd_instance = dict(title="USD Expense", data_source=EXPENSE, format=USD)
args_for_shared_usd_instance.update(common_arguments)
expense_report_usd = Report(**args_for_shared_usd_instance)

page1.add(Report(**args_for_shared_usd_instance))
page2.add(Report(**args_for_shared_usd_instance))

Лучшее наименование, может сделать его удобным. Может быть, есть лучшее дизайнерское решение.

0 голосов
/ 16 января 2012

Я не вижу причин, почему вы не должны просто использовать частичную функцию.

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