Шаблон проектирования рабочего процесса Python - PullRequest
19 голосов
/ 26 октября 2011

Я работаю над дизайном программного обеспечения, и я застрял между отсутствием представления о том, что я делаю, и ощущением, будто я заново изобретаю колесо.

Моя ситуацияследующее: я разрабатываю научную утилиту с интерактивным интерфейсом.Пользовательский ввод должен вызывать визуальную обратную связь (дух), некоторую ее непосредственно, т. Е. Редактировать геометрию домена, а часть - как можно скорее, не блокируя взаимодействие с пользователем, скажем, путем решения некоторого PDE в указанном домене.

Если я нарисую диаграмму всех операций, которые мне нужно выполнить, я получу этот довольно плотный график, предоставляющий все виды возможностей для параллелизма и кеширования / повторного использования частичных результатов.Поэтому я хочу, прежде всего, использовать этот параллелизм прозрачным способом (выбранные подзадачи выполняются в отдельных процессах, в результате чего внешне «присоединяются» нижестоящие задачи, ожидающие готовности всех своих входных данных), плюс требуется только пересчитывать те входные ветви, которыена самом деле их входные данные изменились

pyutilib.workflow, кажется, наиболее близок к тому, что я ищу, за исключением, конечно, того, что это не так (кажется, для начала не выполнялось никаких подпроцесс).Это кажется довольно разочаровывающим;хотя я не инженер по программному обеспечению, я говорю, что я не прошу здесь ничего сумасшедшего.

Еще один сложный фактор - это желаемая тесная интеграция с пользовательским интерфейсом, которую другие научные решения для рабочих процессов кажутся не разработанными.обрабатывать.Например, я хотел бы передать событие перетаскивания через узел преобразования для дальнейшей обработки.Узел преобразования имеет два входа;порт ввода состояния аффинного преобразования и класс pointset, который знает, что с ним делать.Если входной порт аффинного преобразования «грязный» (ожидает обновления своих зависимостей), событие следует удерживать до тех пор, пока оно не станет доступным.Но когда событие прошло узел, порт входа события должен быть помечен как обработанный, поэтому он не обновляется, когда аффинное преобразование изменяется из-за дальнейшего ввода данных пользователем.Это просто пример одной из многих возникающих проблем, которые я не вижу, чтобы меня нигде не интересовали.Или что делать, когда долгожданная ветвь, соединяющая ветвления, получает новые входные данные, когда она находится в процессе обработки предыдущего ввода.

Итак, мой вопрос: вам не знакомы некоторые хорошие книги / статьи ошаблоны проектирования рабочего процесса, которые я должен прочитать?Или я пытаюсь втиснуть квадратный колышек в круглое отверстие, и вы знаете совершенно другой шаблон дизайна, о котором я должен знать?Или пакет python, который делает то, что я хочу, независимо от модных слов, в которые он одет?

Я использовал собственное решение поверх enthought.traits, но я тоже не совсем доволен этимКак это похоже на грубое и дрянное изобретение колеса.За исключением того, что я не могу найти какие-либо колеса в Интернете.

ПРИМЕЧАНИЕ. Я не ищу веб-фреймворков, графических дизайнеров рабочих процессов или каких-либо специальных инструментов.Просто концептуально что-то вроде pyutilib.workflow, но включающее в себя документацию и набор функций, с которыми я могу работать.

# # # РЕДАКТИРОВАТЬ: это то место, где я нахожусь после продолжительного чтения и размышлений над проблемой: # # #

Требования, предъявляемые к «архитектуре рабочего процесса», слишком разнообразны, чтобы существовать один ботинок, который подходит всем.Хотите ли вы тесную интеграцию с дисковым хранилищем, тесную интеграцию с веб-фреймворками, асинхронность, сочетание пользовательской логики конечного автомата для распределения задач?Все они являются действительными требованиями, и они в значительной степени несовместимы или создают бессмысленные миксы.

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

То, что я ищу (и начал писать сам, будет довольно круто) будет выглядеть так: в его основе лежит общий не зависящий от реализации мини-язык объявления рабочих процессов, основанный на декораторах и некоторых метаданных -magic, чтобы преобразовать оператор, подобный приведенному ниже, в объявление рабочего процесса, содержащее всю необходимую информацию:

@composite_task(inputs(x=Int), outputs(z=Float))
class mycompositetask:
    @task(inputs(x=Int), outputs(y=Float))
    def mytask1(x):
        return outputs( y = x*2 )
    @task(inputs(x=Int, y=Float), outputs(z=Float))
    def mytask2(x, y):
        return outputs( z = x+y )
    mytask1.y = mytask2.y   #redundant, but for illustration; inputs/outputs matching in name and metadata autoconnect

То, что возвращают декораторы, это задача / compositetask / workflow объявление класс. Вместо просто ограничений типов, другие метаданные, необходимые для данного типа рабочего процесса, легко добавляются в синтаксис.

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

В простейшем воплощении wed имеет что-то вроде:

wf   = workflow_factory(mycompositetask)
wf.z = lambda result: print result   #register callback on z-output socket
wf.x = 1    #feed data into x input-socket

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

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

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

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

1 Ответ

18 голосов
/ 29 октября 2011

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

Как есть слона?

Уровень A: разработка программного обеспечения

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

В таких сложных задачах, как ваша, вызовы вне уровня пользовательского интерфейса обычно:

  1. Запланируйте некоторыеработа - команда должна быть поставлена ​​в очередь на уровне smart , чтобы ее можно было подхватить при каждом получении.
  2. Чтение результатов - результаты должны быть поставлены в очередь в smart слой, чтобы их можно было просто «вытянуть» и визуализировать.
  3. Отмена / остановка / выход - просто поднять флаг. Умный слой должен время от времени проверять этот флаг.

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

Уровень B: тяжелый умный слой

Не существует "лучшей" основы для«любой» вид трудной работы.

Поэтому я бы предложил вам спроектировать feed (с помощью пользовательского интерфейса) этого слоя как можно более простым, без каркасовучаствует.

Внутренне, вы можете реализовать его с помощью некоторых фреймворков, но в будущем у вас будет возможность перепроектировать трудолюбивых элементов по мере необходимости.Например, в будущем вы могли бы:

  • дать некоторую математику графическому процессору
  • поделиться задачами с фермами серверов
  • задействовать облачные вычисления

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

Трудно сказать наверняка, но мне кажется, что у вас нет фреймворка Silver Bullet для вашей задачи.Таким образом, вы должны найти надежные инструменты (например, потоки и очереди) для реализации хороших методов проектирования (например, разъединение).

РЕДАКТИРОВАТЬ в ответ на ваше редактирование

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

Подсказка заключается в том, что вы предложили наиболее общую задачу, которая будет определена Int и Float.Это может сделать вас счастливыми на сегодня, но завтра не получится.Точно как привязка к супер-абстрактному фреймворку.

Путь правильный - иметь тяжелую базу «задач» в вашем дизайне.Но он не должен определять Int или Float.Сосредоточьтесь на вышеупомянутых «начало», «читать» и «остановить» вместо этого.Если вы не видите размер слона, который вы едите, то вы можете не съесть его и в конечном итоге голодать:)

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

class AnySuperPowerfulTask:
    def run():
        scheduleForAThreadToRunMe()
    def cancel():
        doTheSmartCancellationSoNobodyWouldCrash()

Это дает вам основу - нейтральную, но чистую и отделенную с точки зрения уровня А. (10).

Тем не менее, вам понадобится какая-то настройка задачи и получениереальный результат, верно?Конечно, это попадет на уровень B мышления.Это было бы специфично для задачи (или для группы задач, реализованных в качестве промежуточной базы).Конечное задание может выглядеть следующим образом:

class CalculatePossibilitiesToSaveAllThePandas(SuperPowerfulTask):
    def __init__(someInt, someFloat, anotherURL, configPath):
        anythingSpecificToThisKindOfTasks()
    def getResults():
        return partiallyCalculated4DimensionalEvolutionGraphOfPandasInOptimisticEnvoronment()

(образцы python намеренно некорректны, чтобы сосредоточиться на design , а не на синтаксисе).

Уровень C - абстракция-нирвана

Похоже, этот уровень следует упомянуть в этом посте.

Да, есть такая ловушка, которую могут подтвердить многие хорошие дизайнеры. Состояние, в котором вы можете бесконечно (и без каких-либо результатов) искать «универсальное решение», т. Е. серебряная пуля ). Я предлагаю вам взглянуть на это, а затем быстро уйти, пока не стало слишком поздно;) Попасть в эту ловушку не стыдно - это нормальная стадия развития величайших дизайнеров. По крайней мере, я пытаюсь в это поверить:)

РЕДАКТИРОВАТЬ 2

Вы сказали: «Я работаю над частью дизайна программного обеспечения, и я застрял между отсутствием какой-либо идеи, что я делаю, и ощущением, будто я заново изобретаю колесо».

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

Уровень D - я застрял

Предложение. Выйдите из здания. Пройдите в кафе по соседству, закажите лучший кофе и сядьте. Задайте себе вопрос «Что мне нужно?». Обратите внимание, что это отличается от вопроса «Что я хочу?». Пингуйте его, пока не устраните неправильных ответов и начните наблюдать правильных ответов:

Неправильно ответы:

  1. Мне нужен каркас, который будет делать X, Y и Z.
  2. Мне нужна отвертка, которая может разгоняться до 200 миль в час и собирать лес на ферме поблизости.
  3. Мне нужна удивительная внутренняя структура, которую мой пользователь никогда не увидит.

Правильно отвечает (простите, если я неправильно понял вашу проблему):

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