Посредник (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

Паттерн абстрактная фабрика на 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

Паттерн Фабричный метод (Factory Method) на Python

Фабричный метод — это порождающий паттерн проектирования, который позволяет подклассам изменять создаваемый объект, в зависимости от контекста. Объединяя сущности в обобщенную абстракцию.

Реализация паттерна фабричный метод на Python

# coding: utf-8

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


class ODFDocument(Document):
    def show(self):
        print 'Open document format'


class MSOfficeDocument(Document):
    def show(self):
        print 'MS Office document format'


class Application(object):
    def create_document(self, type_):
        # параметризованный фабричный метод `create_document`
        raise NotImplementedError()


class MyApplication(Application):
    def create_document(self, type_):
        if type_ == 'odf':
            return ODFDocument()
        elif type_ == 'doc':
            return MSOfficeDocument()


app = MyApplication()
app.create_document('odf').show()  # Open document format
app.create_document('doc').show() # MS Office document format

Паттерн Заместитель (Proxy) на Python

Заместитель (proxy) — еще один структурный паттерн, схожий с фасадом или адаптером, только у прокси интерфейс полностью повторяет интерфейс замещающего объекта. Используется для отложенных действий над самим объектом, ограничения доступа к объекту, логирования и т.д.

Пример реализации паттерна Заместитель (proxy) на Python.

# coding: utf-8

from functools import partial


class ImageBase(object):
    """Абстрактное изображение"""
    @classmethod
    def create(cls, width, height):
        """Создает изображение"""
        return cls(width, height)

    def draw(self, x, y, color):
        """Рисует точку заданным цветом"""
        raise NotImplementedError()

    def fill(self, color):
        """Заливка цветом"""
        raise NotImplementedError()

    def save(self, filename):
        """Сохраняет изображение в файл"""
        raise NotImplementedError()


class Image(ImageBase):
    """Изображение"""
    def __init__(self, width, height):
        self._width = int(width)
        self._height = int(height)

    def draw(self, x, y, color):
        print 'Рисуем точку; координаты: (%d, %d); цвет: %s' % (x, y, color)

    def fill(self, color):
        print 'Заливка цветом %s' % color

    def save(self, filename):
        print 'Сохраняем изображение в файл %s' % filename


class ImageProxy(ImageBase):
    """
    Заместитель изображения.
    Откладывает выполнение операций над изображением до момента его сохранения.
    """
    def __init__(self, *args, **kwargs):
        self._image = Image(*args, **kwargs)
        self.operations = []

    def draw(self, *args):
        func = partial(self._image.draw, *args)
        self.operations.append(func)

    def fill(self, *args):
        func = partial(self._image.fill, *args)
        self.operations.append(func)

    def save(self, filename):
        # выполняем все операции над изображением
        map(lambda f: f(), self.operations)
        # сохраняем изображение
        self._image.save(filename)


img = ImageProxy(200, 200)
img.fill('gray')
img.draw(0, 0, 'green')
img.draw(0, 1, 'green')
img.draw(1, 0, 'green')
img.draw(1, 1, 'green')
img.save('image.png')

# Заливка цветом gray
# Рисуем точку; координаты: (0, 0); цвет: green
# Рисуем точку; координаты: (0, 1); цвет: green
# Рисуем точку; координаты: (1, 0); цвет: green
# Рисуем точку; координаты: (1, 1); цвет: green
# Сохраняем изображение в файл image.png