Паттерны проектирования для реализации связей отправитель-получатель

Цепочка обязанностей

передаёт запрос последовательно через цепочку потенциальных получателей, ожидая, что какой-то из них обработает запрос.

Команда

устанавливает косвенную одностороннюю связь от отправителей к получателям.

Посредник

убирает прямую связь между отправителями и получателями, заставляя их общаться опосредованно, через себя.

Наблюдатель

передаёт запрос одновременно всем заинтересованным получателям, но позволяет им динамически подписываться или отписываться от таких оповещений.

Посредник (Controller, Mediator)

Посредник (Controller)
Поведенческий паттерн проектирования позволяет разлепить связи между классами и берет на себя функцию связывания. Классы будут общаться через класс-посредник.

Пример реализации паттерна Посредник на python

# coding: utf-8


class WindowBase(object):
    def show(self):
        raise NotImplementedError()

    def hide(self):
        raise NotImplementedError()


class MainWindow(WindowBase):
    def show(self):
        print 'Show MainWindow'

    def hide(self):
        print 'Hide MainWindow'


class SettingWindow(WindowBase):
    def show(self):
        print 'Show SettingWindow'

    def hide(self):
        print 'Hide SettingWindow'


class HelpWindow(WindowBase):
    def show(self):
        print 'Show HelpWindow'

    def hide(self):
        print 'Hide HelpWindow'


class WindowMediator(object):
    def __init__(self):
        self.windows = dict.fromkeys(['main', 'setting', 'help'])

    def show(self, win):
        for window in self.windows.itervalues():
            if not window is win:
                window.hide()
        win.show()

    def set_main(self, win):
        self.windows['main'] = win

    def set_setting(self, win):
        self.windows['setting'] = win

    def set_help(self, win):
        self.windows['help'] = win


main_win = MainWindow()
setting_win = SettingWindow()
help_win = HelpWindow()

med = WindowMediator()
med.set_main(main_win)
med.set_setting(setting_win)
med.set_help(help_win)

main_win.show()  # Show MainWindow

med.show(setting_win)
# Hide MainWindow
# Hide HelpWindow
# Show SettingWindow

med.show(help_win)
# Hide MainWindow
# Hide SettingWindow
# Show HelpWindow

Состояние (State)

Состояние (State)
Это поведенческий паттерн, при котором объект в разных своих состояниях ведет себя по-разному. Набор состояний обычно конечен и определен.
Реализация паттерна состоит в введении классов-состояний с общим интерфейсом.

Реализация паттерна проектирования Состояние на python

# coding: utf-8

class LampStateBase(object):
    """Состояние лампы"""
    def get_color(self):
        raise NotImplementedError()


class GreenLampState(LampStateBase):
    def get_color(self):
        return 'Green'


class RedLampState(LampStateBase):
    def get_color(self):
        return 'Red'


class BlueLampState(LampStateBase):
    def get_color(self):
        return 'Blue'


class Lamp(object):
    def __init__(self):
        self._current_state = None
        self._states = self.get_states()

    def get_states(self):
        return [GreenLampState(), RedLampState(), BlueLampState()]

    def next_state(self):
        if self._current_state is None:
            self._current_state = self._states[0]
        else:
            index = self._states.index(self._current_state)
            if index < len(self._states) - 1:
                index += 1
            else:
                index = 0
            self._current_state = self._states[index]
        return self._current_state

    def light(self):
        state = self.next_state()
        print state.get_color()


lamp = Lamp()
[lamp.light() for i in range(3)]
# Green
# Red
# Blue
[lamp.light() for i in range(3)]
# Green
# Red
# Blue

Итератор (Iterator)

Итератор (Iterator)

Это один из поведенческих паттернов проектирования/, созданный для обхода коллекций и упрощения классов хранения данных, вынося реализацию (или разные реализации) обхода в другие классы.

# coding: utf-8

class IteratorBase(object):
    """Базовый класс итератора"""
    def first(self):
        """Возвращает первый элемент коллекции.
        Если элемента не существует возбуждается исключение IndexError."""
        raise NotImplementedError()

    def last(self):
        """Возвращает последний элемент коллекции.
        Если элемента не существует возбуждается исключение IndexError."""
        raise NotImplementedError()

    def next(self):
        """Возвращает следующий элемент коллекции"""
        raise NotImplementedError()

    def prev(self):
        """Возвращает предыдущий элемент коллекции"""
        raise NotImplementedError()

    def current_item(self):
        """Возвращает текущий элемент коллекции"""
        raise NotImplementedError()

    def is_done(self, index):
        """Возвращает истину если элемент с указанным индексом существует, иначе ложь"""
        raise NotImplementedError()

    def get_item(self, index):
        """Возвращает элемент коллекции с указанным индексом, иначе выбрасывает исключение IndexError"""
        raise NotImplementedError()


class Iterator(IteratorBase):
    def __init__(self, list_=None):
        self._list = list_ or []
        self._current = 0

    def first(self):
        return self._list[0]

    def last(self):
        return self._list[-1]

    def current_item(self):
        return self._list[self._current]

    def is_done(self, index):
        last_index = len(self._list) - 1
        return 0 <= index <= last_index

    def next(self):
        self._current += 1
        if not self.is_done(self._current):
            self._current = 0
        return self.current_item()

    def prev(self):
        self._current -= 1
        if not self.is_done(self._current):
            self._current = len(self._list) - 1
        return self.current_item()

    def get_item(self, index):
        if not self.is_done(index):
            raise IndexError('Нет элемента с индексом: %d' % index)
        return self._list[index]


it = Iterator(['one', 'two', 'three', 'four', 'five'])
print [it.prev() for i in range(5)]  # ['five', 'four', 'three', 'two', 'one']
print [it.next() for i in range(5)] # ['two', 'three', 'four', 'five', 'one']

Команда (Транзакция, Action)

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

Реализация паттерна проектирования Команда на python

# coding: utf-8


class Light(object):
    def turn_on(self):
        print 'Включить свет'

    def turn_off(self):
        print 'Выключить свет'


class CommandBase(object):
    def execute(self):
        raise NotImplementedError()


class LightCommandBase(CommandBase):
    def __init__(self, light):
        self.light = light


class TurnOnLightCommand(LightCommandBase):
    def execute(self):
        self.light.turn_on()


class TurnOffLightCommand(LightCommandBase):
    def execute(self):
        self.light.turn_off()


class Switch(object):
    def __init__(self, on_cmd, off_cmd):
        self.on_cmd = on_cmd
        self.off_cmd = off_cmd

    def on(self):
        self.on_cmd.execute()

    def off(self):
        self.off_cmd.execute()


light = Light()
switch = Switch(on_cmd=TurnOnLightCommand(light),
                off_cmd=TurnOffLightCommand(light))
switch.on()  # Включить свет
switch.off() # Выключить свет

Цепочка обязанностей (Chain of Responsibility)

Цепочка обязанностей (Chain of Responsibility)
— это поведенческий паттерн, который позволяет передавать данные последовательно по обработчикам цепочки.
На практике очень часто используется с паттерном проектирования Компоновщик. Т.к. в дереве элементов легко можно выделить цепочку обработчиков.
Также паттерн проектирования python Цепочка обязанностей очень похож на паттерн Декоратор.
Однако, в отличие от декоратора обработчик может прервать цепочку.

Реализация паттерна проектирования Цепочка обязанностей на python

# coding: utf-8

class HttpHandler(object):
    """Абстрактный класс обработчика"""
    def handle(self, code):
        raise NotImplementedError()


class Http404Handler(HttpHandler):
    """Обработчик для кода 404"""
    def handle(self, code):
        if code == 404:
            return 'Страница не найдена'


class Http500Handler(HttpHandler):
    """Обработчик для кода 500"""
    def handle(self, code):
        if code == 500:
            return 'Ошибка сервера'


class Client(object):
    def __init__(self):
        self._handlers = []

    def add_handler(self, h):
        self._handlers.append(h)

    def response(self, code):
        for h in self._handlers:
            msg = h.handle(code)
            if msg:
                print 'Ответ: %s' % msg
                break
        else:
            print 'Код не обработан'


client = Client()
client.add_handler(Http404Handler())
client.add_handler(Http500Handler())
client.response(400)  # Код не обработан
client.response(404)  # Ответ: Страница не найдена
client.response(500) # Ответ: Ошибка сервера

Отличие сортировки списков в python 2 и python 3

Проблема в том, что python 3 отказывается сортировать списки, содержащие значения None!

x = [1, 2, 3, 5555, -1, 0, None, None]
x.sort()

# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: ‘<‘ not supported between instances of ‘NoneType’ and ‘int’

Вариант обхода этой ситуации — это:
x.sort(key=lambda x: x or -1)

Паттерн Прототип (Prototype) на Python

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


# coding: utf-8

import copy

class Prototype(object):
def __init__(self):
self._objects = {}

def register(self, name, obj):
self._objects[name] = obj

def unregister(self, name):
del self._objects[name]

def clone(self, name, attrs):
obj = copy.deepcopy(self._objects[name])
obj.__dict__.update(attrs)
return obj

class Bird(object):
"""Птица"""

prototype = Prototype()
prototype.register('bird', Bird())

owl = prototype.clone('bird', {'name': 'Owl'})
print type(owl), owl.name # Owl

duck = prototype.clone('bird', {'name': 'Duck'})
print type(duck), duck.name # Duck

Паттерн строитель (builder) на python

Строитель (Builder) — паттерн, порождающий объекты.
Отделяет конструирование сложного объекта от его представления,
так что в результате одного и того же процесса конструирования могут получаться разные представления.
От абстрактной фабрики отличается тем, что делает акцент на пошаговом конструировании объекта.
Строитель возвращает объект на последнем шаге, тогда как абстрактная фабрика возвращает объект немедленно.
Строитель часто используется для создания паттерна компоновщик.

Реализация паттерна Строитель на python

# coding: utf-8


class Builder(object):
    def build_body(self):
        raise NotImplementedError()

    def build_lamp(self):
        raise NotImplementedError()

    def build_battery(self):
        raise NotImplementedError()

    def create_flashlight(self):
        raise NotImplementedError()


class Flashlight(object):
    """Карманный фонарик"""
    def __init__(self, body, lamp, battery):
        self._shine = False  # излучать свет
        self._body = body
        self._lamp = lamp
        self._battery = battery

    def on(self):
        self._shine = True

    def off(self):
        self._shine = False

    def __str__(self):
        shine = 'on' if self._shine else 'off'
        return 'Flashlight [%s]' % shine


class Lamp(object):
    """Лампочка"""


class Body(object):
    """Корпус"""


class Battery(object):
    """Батарея"""


class FlashlightBuilder(Builder):
    def build_body(self):
        return Body()

    def build_battery(self):
        return Battery()

    def build_lamp(self):
        return Lamp()

    def create_flashlight(self):
        body = self.build_body()
        lamp = self.build_lamp()
        battery = self.build_battery()
        return Flashlight(body, lamp, battery)


builder = FlashlightBuilder()
flashlight = builder.create_flashlight()
flashlight.on()
print flashlight  # Flashlight [on]

Паттерн абстрактная фабрика на python

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

# coding: utf-8

class AbstractFactory(object):
def create_drink(self):
raise NotImplementedError()

def create_food(self):
raise NotImplementedError()

class Drink(object):
def __init__(self, name):
self._name = name

def __str__(self):
return self._name

class Food(object):
def __init__(self, name):
self._name = name

def __str__(self):
return self._name

class ConcreteFactory1(AbstractFactory):
def create_drink(self):
return Drink('Coca-cola')

def create_food(self):
return Food('Hamburger')

class ConcreteFactory2(AbstractFactory):
def create_drink(self):
return Drink('Pepsi')

def create_food(self):
return Food('Cheeseburger')

def get_factory(ident):
if ident == 0:
return ConcreteFactory1()
elif ident == 1:
return ConcreteFactory2()

factory = get_factory(1)
print factory.create_drink() # Pepsi
print factory.create_food() # Cheeseburger