html текст
All interests
  • All interests
  • Design
  • Food
  • Gadgets
  • Humor
  • News
  • Photo
  • Travel
  • Video
Click to see the next recommended page
Like it
Don't like
Add to Favorites

Паттерны проектирования без ООП

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

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

Наблюдатель

Нужно обеспечить возможность каким-то объектам подписываться на сообщения, а каким-то эти сообщения отсылать.
Реализуется словарём, который и представляет собой «почту». Ключами будут названия рассылок, а значениями списки подписчиков.
from collections import defaultdict

mailing_list = defaultdict(list)

def subscribe(mailbox, subscriber):
    # Подписывает функцию subscriber на рассылку с именем mailbox
    mailing_list[mailbox].append(subscriber)

def notify(mailbox, *args, **kwargs):
    # Вызывает подписчиков рассылки mailbox, передавая им параметры
    for sub in mailing_list[mailbox]:
        sub(*args, **kwargs)


Теперь можно любые функции подписывать на рассылки. Главное, чтобы интерфейс функций входящих в одну и ту же группу рассылки, был совместим.
def fun(insert):
    print 'FUN %s' % insert

def bar(insert):
    print 'BAR %s' % insert


Подписываем наши функции на рассылки:
>>> subscribe('insertors', fun)
>>> subscribe('insertors', bar)
>>> subscribe('bars', bar)


В любом месте кода вызываем уведомления для этих рассылок и наблюдаем, что все подписчики реагируют на событие:
>>> notify('insertors', insert=123)
FUN 123
BAR 123

>>> notify('bars', 456)
BAR 456


Шаблонный метод

Нужно обозначить каркас алгоритма и дать возможность пользователям переопределять определенные шаги в нём.
Функции высшего порядка, такие как map, filter, reduce по сути и являются такими шаблонами. Но давайте посмотрим, как можно провернуть такое же самому.
def approved_action(checker, action, obj):
    # Шаблон, который выполняет над объектом obj действие action,
    # если проверка checker дает положительный результат
    if checker(obj):
        action(obj)

import os
def remove_file(filename):
    approved_action(os.path.exists, os.remove, filename)

import shutil
def remove_dir(dirname):
    approved_action(os.path.exists, shutil.rmtree, dirname)

Имеем функции удаления файла и папки, проверяющие предварительно, есть ли нам чего удалять.
Если вызов «шаблона» напрямую кажется противоречащим паттерну, можно определять функции с помощью каррирования. Ну и ввести до кучи возможность «переопределения» не всех частей алгоритма.
def approved_action(obj, checker=lambda x: True, action=lambda x: None):
    if checker(obj):
        action(obj)

from functools import partial
remove_file = partial(approved_action, checker=os.path.exists, action=os.remove)
remove_dir = partial(approved_action, checker=os.path.exists, action=shutil.rmtree)

import sys
printer = partial(approved_action, action=sys.stdout.write)


Состояние

Нужно обеспечить разное поведение объекта в зависимости от его состояния.
Давайте представим, что нам нужно описать процесс выполнения заявки, который может потребовать несколько циклов согласований.
from random import randint
# Функции, выполняющие работу в каждом из состояний.
# Аргументом ко всем является обрабатываемая заявка
# Вызовы randint эмулируют логику, принимающую какие-то решения в зависимости от внешних обстоятельств

def start(claim):
    print u'заявка подана'
    claim['state'] = 'analize'

def analize(claim):
    print u'анализ заявки'
    if randint(0, 2) == 2:
        print u'заявка принята к исполнению'
        claim['state'] = 'processing'
    else:
        print u'требуется уточнение'
        claim['state'] = 'clarify'

def processing(claim):
    print u'проведены работы по заявке'
    claim['state'] = 'close'

def clarify(claim):
    if randint(0, 4) == 4:
        print u'пользователь отказался от заявки'
        claim['state'] = 'close'
    else:
        print u'уточнение дано'
        claim['state'] = 'analize'

def close(claim):
    print u'заявка закрыта'
    claim['state'] = None


# Определение конечного автомата. Какие функции в каком состоянии вызывать
state = {'start': start,
         'analize': analize,
         'processing': processing,
         'clarify': clarify,
         'close': close}

# Запуск заявки в работу
def run_claim():
    claim = {'state': 'start'} # Новая заявка
    while claim['state'] is not None: # Крутим машину, пока заявка не закроется
        fun = state[claim['state']] # определяем запускаемую функцию
        fun(claim)

Как видим, основную часть кода занимает «бизнес-логика», а не оверхед на применение паттерна. Автомат легко расширять и изменять, просто добавляя/заменяя функции в словаре state.

Запустим пару раз, чтобы убедиться в работоспособности:
>>> run_claim()
заявка подана
анализ заявки
требуется уточнение
уточнение дано
анализ заявки
заявка принята к исполнению
проведены работы по заявке
заявка закрыта

>>> run_claim()
заявка подана
анализ заявки
требуется уточнение
пользователь отказался от заявки
заявка закрыта


Команда

Задача – организовать «обратный вызов». То есть, чтобы вызываемый объект мог из своего кода обратиться к вызывающему.
Этот паттерн видимо возник из-за ограничений статичных языков. Функциональщики бы его даже звания паттерна не удостоили. Есть функция – пожалуйста, передавай её куда хочешь, сохраняй, вызывай.
def foo(arg1, arg2): # наша команда
    print 'FOO %s, %s' (arg1, arg2)

def bar(cmd, arg2):
    # Приемник команды. Ничего не знает о функции foo...
    print 'BAR %s' % arg2
    cmd(arg2 * 2) # ...но вызывает её


В исходных задачах паттерна Команда есть и возможность передавать некоторые параметры объекту-команде заранее. В зависимости от удобства, решается либо каррированием…
>>> from functools import partial
>>> bar(partial(foo, 1), 2)
BAR 2
FOO 1, 4

…либо заворачиванием в lambda
>>> bar(lambda x: foo(x, 5), 100)
BAR 100
FOO 200, 5


Общий вывод

Не обязательно городить огород из абстрактных классов, конкретных классов, интерфейсов и т.д. Минимальные возможности обращения с функциями как с объектами первого класса, уже позволяют довольно лаконично применять те же шаблоны проектирования. Иногда даже не замечая этого :)
Читать дальше
Twitter
Одноклассники
Мой Мир

материал с habrahabr.ru

3

      Add

      You can create thematic collections and keep, for instance, all recipes in one place so you will never lose them.

      No images found
      Previous Next 0 / 0
      500
      • Advertisement
      • Animals
      • Architecture
      • Art
      • Auto
      • Aviation
      • Books
      • Cartoons
      • Celebrities
      • Children
      • Culture
      • Design
      • Economics
      • Education
      • Entertainment
      • Fashion
      • Fitness
      • Food
      • Gadgets
      • Games
      • Health
      • History
      • Hobby
      • Humor
      • Interior
      • Moto
      • Movies
      • Music
      • Nature
      • News
      • Photo
      • Pictures
      • Politics
      • Psychology
      • Science
      • Society
      • Sport
      • Technology
      • Travel
      • Video
      • Weapons
      • Web
      • Work
        Submit
        Valid formats are JPG, PNG, GIF.
        Not more than 5 Мb, please.
        30
        surfingbird.ru/site/
        RSS format guidelines
        500
        • Advertisement
        • Animals
        • Architecture
        • Art
        • Auto
        • Aviation
        • Books
        • Cartoons
        • Celebrities
        • Children
        • Culture
        • Design
        • Economics
        • Education
        • Entertainment
        • Fashion
        • Fitness
        • Food
        • Gadgets
        • Games
        • Health
        • History
        • Hobby
        • Humor
        • Interior
        • Moto
        • Movies
        • Music
        • Nature
        • News
        • Photo
        • Pictures
        • Politics
        • Psychology
        • Science
        • Society
        • Sport
        • Technology
        • Travel
        • Video
        • Weapons
        • Web
        • Work

          Submit

          Thank you! Wait for moderation.

          Тебе это не нравится?

          You can block the domain, tag, user or channel, and we'll stop recommend it to you. You can always unblock them in your settings.

          • habrahabr.ru
          • домен habrahabr.ru

          Get a link

          Спасибо, твоя жалоба принята.

          Log on to Surfingbird

          Recover
          Sign up

          or

          Welcome to Surfingbird.com!

          You'll find thousands of interesting pages, photos, and videos inside.
          Join!

          • Personal
            recommendations

          • Stash
            interesting and useful stuff

          • Anywhere,
            anytime

          Do we already know you? Login or restore the password.

          Close

          Add to collection

             

            Facebook

            Ваш профиль на рассмотрении, обновите страницу через несколько секунд

            Facebook

            К сожалению, вы не попадаете под условия акции