2009-06-05 3 views
4

Мне часто поручается обработка списка задач. Обычно для этого требуется, чтобы я что-то искал программным способом и хранил его где-то (база данных, общий ресурс файла, ftp и т. Д.). Затем мне нужно обработать текстовый файл, xml, sql result set или что-то еще, и сохранить мои результаты.Есть ли какие-либо шаблоны дизайна для устранения «моя программа на самом деле просто большая петля»?

Например:

У меня есть база данных, которая детализирует список кучи сетей, которые могут или не могут быть подключены к моей сети. Если нет, я должен использовать Windows RAS для подключения к серверу в сети. В некоторых сетях может быть больше одного компьютера. Я должен вытащить файл конфигурации из каждой системы (имеет ли сеть один или несколько) и сохранить это в базе данных. Затем я должен разобрать определенную информацию из этих файлов и сохранить ее в базе данных. Мне также нужно отслеживать, когда система была проверена и были ли какие-либо ошибки.

У меня есть абстракция базы данных, она хороша аккуратно и организована. Моя база данных готова для данных, всех ошибок, проверок для конкретных систем, ошибок и т. Д.

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

Вот и пример:

static void main() 
{ 

    var networks = NetworkLocationsRepository.GetAll(); 

    foreach(var network in networks) 
    { 
    ConfigurationFile file = null; 

    if(!network.IsConnected) 
    { 
     ConnectToNetwork(network); 
    } 

    foreach(var system in network.Systems) 
    { 
     file = GetConfigFile(system); 
     ConfigurationFileRepository.Add(file); 
     var configData = ParseConfiguration(file); 
     ConfigurationFileRepository.UpdateData(file, configData);  
    } 
    } 
} 

Редактировать

В ответ на замечания, я не настолько обеспокоен с петлей (несмотря на то, что вот что мое название сосредотачивается на). Я знаю, что, поскольку у меня есть набор элементов для итерации, в какой-то форме или форме, у меня будет цикл. Действительно, меня больше интересует все, что попадает в цикл. Этот пример довольно простой, но, скажем, у меня есть 10 различных задач для каждой системы? Разумеется, не самый лучший подход - использовать кучу операторов if, чтобы определить, должны ли выполняться задачи, заполненные в один цикл foreach?

+4

lol - Извините, но название смешно. –

+0

эй, я должен как-то привлечь ваше внимание. – scottm

+0

Хммм, я думаю, я действительно не понимаю проблему. – Dolphin

ответ

2

Мне нравится Ayende's pipes and filters. Вы бы определили все свои «петли» и связали их.

+0

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

+0

@scotty, похоже, что вы собираетесь использовать отбойный молоток, когда это сделает долото. Конечно, отбойный молот веселее. ;) – dss539

+0

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

-1

Рассматриваете ли вы образец итератора? http://en.wikipedia.org/wiki/Iterator_pattern

+0

Для «foreach» требуется итератор, поэтому он использует его дважды, технически. –

+0

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

+0

Я не думаю, что это то, что мне нужно. – scottm

2

Используйте шаблон команды.

1

Nah, все программисты, как и ваш код. (/ jk)

Серьезно: нет, не совсем. Если вы не хотите разбить детали на маленькие кусочки ... они все равно будут работать в цикле. Даже приложение MVC использует цикл, хотя он проходит через все части каждой итерации. Поэтому даже если вы выясните способ упростить свой основной цикл, это все равно будет какой-то вариант цикла, если это не однопроходный.

(Даже если вы используете шаблон дизайна, чтобы «упростить» это, вы все равно будете использовать цикл!)

+0

Я знаю, что по существу мне придется перебирать сети. Мне любопытно, есть ли лучший способ справиться с тем, что должно произойти для каждой сети, то есть, если (network.Connected) connect(); Я могу просто сказать, как выполнить эту задачу для этой сети. И, надеюсь, через какой-то шаблон, независимо от того, нужно ли мне подключаться или если есть несколько систем, все будет выяснено при построении классов. – scottm

+0

Да, scotty есть способ абстрагироваться от этих линий. Процесс выглядит следующим образом: выделите код, щелкните правой кнопкой мыши, рефакторинг, метод извлечения. – dss539

+0

@ dss529, но тогда мой вопрос: «Куда я положу эти методы?». Должны ли они быть частью класса программы, который содержит метод Main, должен ли быть только класс статических вспомогательных методов? – scottm

2

Ничего плохого с петлями :)

Но в любом случае, я не уверен, что это улучшает читаемость в вашем случае, но его вполне можно абстрагироваться от петель общей сложности (примечания: непроверенный код, вероятно, содержит синтаксические ошибки):

public static void iter<T>(IEnumerable<T> items, Action<T> f) 
{ 
    foreach(T item in item) 
    { 
     f(item); 
    } 
} 

public static void Process() 
{ 
    iter(NetworkLocationsRepository.GetAll(), network => { 
     ConfigurationFile file = null; 

     if(!network.IsConnected) 
     { 
      ConnectToNetwork(network); 
     } 

     iter(network, system => { 
      file = GetConfigFile(system); 
      ConfigurationFileRepository.Add(file); 
      var configData = ParseConfiguration(file); 
      ConfigurationFileRepository.UpdateData(file, configData);     
     }); 
    }); 
} 

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

public static void iter<T>(IEnumerable<T> items, Action<T> f) 
{ 
    foreach(T item in item) 
    { 
     Threading.QueueUserWorkItem(new WaitCallBack(() => f(item))); 
    } 
} 

... тогда ваш код многопоточный без каких-либо изменений в ваш метод процесса :)


[Редактировать to add]: Прежде чем кто-нибудь скажет: «Эй, вы использовали цикл», помните, что весь шаблон дизайна - абстрагировать детали реализации и сделать их прозрачными для клиента.

Предположим, мы говорили не о циклах, а вместо этого говорили о инструкциях swtich, где OP сказал: «Я использую операторы switch везде, и я постоянно включаю эти три значения, как я могу это исправить? " Ответ, очевидно, заключается в том, чтобы вывести реализацию в несколько классов, которые имеют общий интерфейс и реализуют логику, необходимую для каждого конкретного значения; то вы должны использовать фабрику, чтобы вернуть требуемую реализацию с помощью инструкции оператора switch, чтобы выбрать правильную реализацию.

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

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

+0

Но ... вы повторили его через цикл в общем вызове! Общий или нет, это все еще «loopy». –

0

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

+0

Это всего лишь пример. На удаленных системах могут быть файлы, которые мне нужно проверить, начать процессы, файлы, которые мне нужно загрузить, и т. Д. Итак, у моего main() будет тонна операторов if (network.NeedsFile) и if (network. IfStartProcess), или если (network.system ["COMPUTER1"]. NeedsFile), если (network.System ["COMPUTER2"]. NeedsFile). и т.д. – scottm

0

Если у вас есть элементы «n» для обработки, цикл не кажется необоснованным.

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

  1. У вас есть «n» элементы работы, и вы обрабатываете их последовательно. Поскольку (я собираюсь), они в основном связаны с сетью, почему бы не размножаться на несколько потоков? По крайней мере, вам не придется ждать, пока элементы «n» будут завершены последовательно.
  2. Это немного от стены (или, возможно, немного не по теме или, может быть, немного ползучего). Space-based architectures полагаются на задачи сброса в пространство, а затем рабочие потоки вытягивают эти задачи из пространства, обрабатывают их и возвращают результаты (в простом случае). Опять же, вы не зацикливаете и не обрабатываете, а полагаетесь на рабочие потоки m, чтобы сделать это для вас.

Я пришел из мира Java и поэтому использовал бы фабрики-исполнители и Javaspaces для 1 и 2 выше. Признаюсь, я не могу консультироваться по конкретным c-острым вещам.

+1

Это просто звучит как производитель/потребитель ... почему причудливое имя? – dss539

+0

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

+0

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

1

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

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

Но это всего лишь сущность полиморфного программирования, поэтому он может не сообщать вам ничего, чего вы еще не знаете.

0

Обычно я использую подход на основе агента, в котором приложение-хостинг, например, служба windows, порождает отдельные потоки, каждый из которых отвечает за мониторинг своей собственной системы. Таким образом, если одна система реагирует особенно медленно, она не остановит обработку/мониторинг других систем. Мы использовали этот подход для различных приложений (мониторинг, сбор данных, системы управления и т. Д.). Обычно мы направляемся по этому пути, когда у нас есть аналогичные операции, которые необходимо выполнять в различных системах или источниках.

0

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

  • сеть задача является объектом с do_me метод, который принимает одну сеть в качестве аргумента.

  • Системная задача - это объект с методом do_me, который принимает одну систему в качестве аргумента.

  • Задача файла - это объект с методом do_me, который принимает один файл в качестве аргумента.

Тогда вам нужны способы слагающих задач:

  • for_all_systems метод принимает сетевую задачу N и возвращает системную задачу S. Задача N выполняется путем итерации по каждой системе в сети и выполнения задачи S в этой системе.

  • do_on_config_file метод принимает файл задачу F и создает системную задачу S. Задача S выполняется в системе, выполняя задачу F в файле конфигурации этой системы.

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