+7 (495) 229-0436   shopadmin@itshop.ru 119334, г. Москва, ул. Бардина, д. 4, корп. 3
 
 
Вход
 
 
Каталог
 
 
Подписка на новости
Новости ITShop
Windows 7 и Office: Новости и советы
Обучение и сертификация Microsoft
Вопросы и ответы по MSSQLServer
Delphi - проблемы и решения
Adobe Photoshop: алхимия дизайна
 
Ваш отзыв
Оцените качество магазина ITShop.ru на Яндекс.Маркете. Если вам нравится наш магазин - скажите об этом Google!
 
 
Способы оплаты
 
Курс расчета
 
 1 у.е. = 91.78 руб.
 
 Цены показывать:
 
 
 
 
  
Новости, статьи, акции
 

Абстрагирование потока управления

07.11.2012 11:21
Suor

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

Что значит абстрагирование потока управления или "control flow", как выражаются наши заморские друзья? В случае, когда никто не выпендривается, потоком занимаются управляющие конструкции. Иногда этих управляющих конструкций недостаточно и мы дописываем свои, абстрагирующие нужное нам поведение программы. Это просто в языках вроде lisp, ruby или perl, но и в других языках это возможно, например, с помощью функций высшего порядка.

Абстракции


Начнём с начала. Что нужно сделать, чтобы построить новую абстракцию?
  1. Выделить какой-то кусок функциональности или поведения.
  2. Дать ему имя.
  3. Реализовать его.
  4. Спрятать реализацию за выбранным именем.

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

Что делать если ваш язык недостаточно гибок? Ничего страшного, вместо реализации вы можете просто подробно описать свой приём, сделать его популярным и, таким образом, породить новый "паттерн проектирования". Или просто перейти на более мощный язык, если создание паттернов вас не прельщает.

Но довольно теории, займёмся делом…

Пример из жизни


Обычный код на питоне (взят из реального проекта с минимальными изменениями):

urls = ...
photos = []

for url in urls:
    for attempt in range(DOWNLOAD_TRIES):
        try:
            photos.append(download_image(url))
            break
        except ImageTooSmall:
            pass # пропускаем урл мелкой картинки
        except (urllib2.URLError, httplib.BadStatusLine, socket.error), e:
            if attempt + 1 == DOWNLOAD_TRIES:
                raise

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

В частности итерация + сбор результатов реализованы во встроенной функции map:

photos = map(download_image, urls)

Попробуем выудить и остальные аспекты. Начнём с пропуска мелких картинок, он мог бы выглядеть так:

@contextmanager
def skip(error):
    try:
        yield
    except error:
        pass

for url in urls:
    with skip(ImageTooSmall):
        photos.append(download_image(url))

Неплохо, но есть недостаток - пришлось отказаться от использования map. Оставим пока эту проблему и перейдём к аспекту устойчивости к сетевым ошибкам. Аналогично предыдущей абстракции можно было бы написать:

with retry(DOWNLOAD_TRIES, (urllib2.URLError, httplib.BadStatusLine, socket.error)):
    # ... do stuff

Только вот это не будет работать, with в питоне не может выполнить свой блок кода более одного раза. Мы уткнулись в ограничения языка и теперь вынуждены либо свернуть и использовать альтернативные решения, либо породить ещё один "паттерн". Замечать подобные ситуации важно, если вы хотите понять различия в языках, и чем один может быть мощнее другого, несмотря на то, что они все полны по Тьюрингу. В ruby и с меньшим удобством в perl мы могли продолжить манипулировать блоками, в лиспе - блоками или кодом (последнее в данном случае, видимо, ни к чему), в питоне нам придётся использовать альтернативный вариант.

Вернёмся к функциям высшего порядка, а точнее к их особой разновидности - декораторам:

@decorator
def retry(call, tries, errors=Exception):
    for attempt in range(tries):
        try:
            return call()
        except errors:
            if attempt + 1 == tries:
                raise

http_retry = retry(DOWNLOAD_TRIES, (urllib2.URLError, httplib.BadStatusLine, socket.error))
harder_download_image = http_retry(download_image)
photos = map(harder_download_image, urls)

Как мы видим, подобный подход хорошо стыкуется с использованием map, также мы получили пару штучек, которые нам ещё когда-нибудь пригодятся - retry и http_retry.

Перепишем skip в том же стиле:

@decorator
def skip(call, errors=Exception):
    try:
        return call()
    except errors:
        return None

skip_small = skip(ImageTooSmall)
http_retry = retry(DOWNLOAD_TRIES, (urllib2.URLError, httplib.BadStatusLine, socket.error))
download = http_retry(skip_small(download_image))
photos = filter(None, map(download, urls))

filter понадобился, чтобы пропустить отброшенные картинки. На самом деле, шаблон filter(None, map(f, seq)) настолько часто встречается, что в некоторых языках есть встроенная функция для такого случая.

Мы тоже можем такую реализовать:

def keep(f, seq):
    return filter(None, map(f, seq))

photos = keep(download, urls)

Что в итоге? Теперь все аспекты нашего кода на виду, легко различимы, изменяемы, заменяемы и удаляемы. А в качестве бонуса мы получили набор абстракций, которые могут быть использованы в дальнейшем. А ещё, надеюсь, я заставил кого-нибудь увидеть новый способ сделать свой код лучше.

Ссылки по теме

  
Помощь
Задать вопрос
 программы
 обучение
 экзамены
 компьютеры
Бесплатный звонок
ICQ-консультанты
Skype-консультанты

Общая справка
Как оформить заказ
Тарифы доставки
Способы оплаты
Прайс-лист
Карта сайта
 
Бестселлеры
Курсы обучения "Atlassian JIRA - система управления проектами и задачами на предприятии"
Microsoft Windows 10 Профессиональная 32-bit/64-bit. Все языки. Электронный ключ
Microsoft Office для Дома и Учебы 2019. Все языки. Электронный ключ
Курс "Oracle. Программирование на SQL и PL/SQL"
Курс "Основы TOGAF® 9"
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год. Электронный ключ
Курс "Нотация BPMN 2.0. Ее использование для моделирования бизнес-процессов и их регламентации"
 

О нас
Интернет-магазин ITShop.ru предлагает широкий спектр услуг информационных технологий и ПО.

На протяжении многих лет интернет-магазин предлагает товары и услуги, ориентированные на бизнес-пользователей и специалистов по информационным технологиям.

Хорошие отзывы постоянных клиентов и высокий уровень специалистов позволяет получить наивысший результат при совместной работе.

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



 

О нас

 
Главная
Каталог
Новинки
Акции
Вакансии
 

Помощь

 
Общая справка
Как оформить заказ
Тарифы доставки
Способы оплаты
Прайс-лист
Карта сайта
 

Способы оплаты

 

Проекты Interface Ltd.

 
Interface.ru   ITShop.ru   Interface.ru/training   Olap.ru   ITnews.ru  
 

119334, г. Москва, ул. Бардина, д. 4, корп. 3
+7 (495) 229-0436   shopadmin@itshop.ru
Проверить аттестат
© ООО "Interface Ltd."
Продаем программное обеспечение с 1990 года