2012-01-16 2 views
1

Моя база кода находится в 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)) 

И в моей базе кода используется уродливая техника. Поскольку нам нужны два отдельных экземпляра для каждой страницы, и потому что мы не хотим дублировать код с длинным списком параметров, который создает отчет, мы просто клонируем (deepcopy in 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 
... 
+0

О клоне и его проблемах. Может быть, вы можете использовать копию аргументов ключевого слова? 'report_args = dict (title = 'Some tile', data_source = ..., columns = ..., format = ...)' then' cost_report1 = Report (** report_args), cost_report2 = Report (** report_args) ' – reclosedev

+0

И мне непонятно, какие типы аргументов вы используете, но если у вас есть метод, который принимает большое количество параметров, я думаю, что в этой ситуации использовать аргументы ключевого слова. Несколько хороших примеров в [SO question] (http://stackoverflow.com/q/1098549/1052325) – reclosedev

ответ

0

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

2

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

class Report(object): 
    def __init__(self, **params): 
     self.params = params 
     ... 
+0

мы используем параметр ключевого слова. Я добавил «Дополнительная информация». –

0

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

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

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

I. Карри - ассоциирования параметров с функцией «рецептов Python« Код ActiveState

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

Смотреть все ОБСУЖДЕНИЕ. Комментарий Ника Перкинса к подклассам «Легкий» аналогичен тому, что я описал.

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

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

0

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

я сделал небольшую библиотеку под названием classical упростить наследование классов такие случаи (только 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 классов, но может быть хорошей идеей, чтобы сохранить оригинальный класс «чистый».

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

Смежные вопросы