Цепочка обязанностей (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) # Ответ: Ошибка сервера

Паттерн Прототип (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

Паттерн фасад (facade) python

Фасад — структурный паттерн проектирования, позволяющий дать интерфейс более высокого уровня к сложной системе.
В отличии от адаптера, используется новый интерфейс.
Большой минус в том, что в данной концепции, фасад может стать godlike, связанным со всей системой.
Иногда фасад превращают в синглтон, т.к. обычно нужен всего 1 фасад.

реализация паттерна
[сode lang=»python»]
# coding: utf-8

class Paper(object):
«»»Бумага»»»
def __init__(self, count):
self._count = count

def get_count(self):
return self._count

def draw(self, text):
if self._count > 0:
self._count -= 1
print text

class Printer(object):
«»»Принтер»»»
def error(self, msg):
print ‘Ошибка: %s’ % msg

def print_(self, paper, text):
if paper.get_count() > 0:
paper.draw(text)
else:
self.error(‘Бумага закончилась’)

class Facade(object):
def __init__(self):
self._printer = Printer()
self._paper = Paper(1)

def write(self, text):
self._printer.print_(self._paper, text)

f = Facade()
f.write(‘Hello world!’) # Hello world!
f.write(‘Hello world!’) # Ошибка: Бумага закончилась
[/code]

паттерн Декоратор (decorator) python

Паттерн Декоратор(decorator) еще один структурный паттерн, который позовляет наделять объекты новыми свойствами и по сути является альтернативой наследованию. В отличии от адаптера не меняет интерфейс!

# coding: utf-8

class Man(object):
    """Человек"""
    def __init__(self, name):
        self._name = name

    def say(self):
        print 'Привет! Меня зовут %s!' % self._name


class Jetpack(object):
    """Реактивный ранец"""
    def __init__(self, man):
        self._man = man

    def __getattr__(self, item):
        return getattr(self._man, item)

    def fly(self):
        # расширяем функциональность объекта добавляя возможность летать
        print '%s летит на реактивном ранце!' % self._man._name


man = Man('Александр')

man_jetpack = Jetpack(man)
man_jetpack.say()  # Привет! Меня зовут Александр!
man_jetpack.fly()  # Виктор летит на реактивном ранце!

Паттерн компоновщик на python

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

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

# coding: utf-8


# Класс представляющий одновременно примитивы и контейнеры
class Graphic(object):
    def draw(self):
        raise NotImplementedError()

    def add(self, obj):
        raise NotImplementedError()

    def remove(self, obj):
        raise NotImplementedError()

    def get_child(self, index):
        raise NotImplementedError()


class Line(Graphic):
    def draw(self):
        print 'Линия'


class Rectangle(Graphic):
    def draw(self):
        print 'Прямоугольник'


class Text(Graphic):
    def draw(self):
        print 'Текст'


class Picture(Graphic):
    def __init__(self):
        self._children = []

    def draw(self):
        print 'Изображение'
        # вызываем отрисовку у вложенных объектов
        for obj in self._children:
            obj.draw()

    def add(self, obj):
        if isinstance(obj, Graphic) and not obj in self._children:
            self._children.append(obj)

    def remove(self, obj):
        index = self._children.index(obj)
        del self._children[index]

    def get_child(self, index):
        return self._children[index]


pic = Picture()
pic.add(Line())
pic.add(Rectangle())
pic.add(Text())
pic.draw()
# Изображение
# Линия
# Прямоугольник
# Текст

line = pic.get_child(0)
line.draw() # Линия

Паттерн мост python

Мост — структурный паттерн, основная задача которого отделить абстракцию(например интерфейс) от реализации (например бэкенд).
яркий пример — это интерфейс «пульт» и реализации «телевизор»

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

# coding: utf-8

class TVBase(object):
    """Абстрактный телевизор"""
    def tune_channel(self, channel):
        raise NotImplementedError()


class SonyTV(TVBase):
    """Телевизор Sony"""
    def tune_channel(self, channel):
        print('Sony TV: выбран %d канал' % channel)


class SharpTV(TVBase):
    """Телевизор Sharp"""
    def tune_channel(self, channel):
        print('Sharp TV: выбран %d канал' % channel)


class RemoteControlBase(object):
    """Абстрактный пульт управления"""
    def __init__(self):
        self._tv = self.get_tv()

    def get_tv(self):
        raise NotImplementedError()

    def tune_channel(self, channel):
        self._tv.tune_channel(channel)


class RemoteControl(RemoteControlBase):
    """Пульт управления"""
    def __init__(self):
        super(RemoteControl, self).__init__()
        self._channel = 0  # текущий канал

    def get_tv(self):
        return SharpTV()

    def tune_channel(self, channel):
        super(RemoteControl, self).tune_channel(channel)
        self._channel = channel

    def next_channel(self):
        self._channel += 1
        self.tune_channel(self._channel)

    def previous_channel(self):
        self._channel -= 1
        self.tune_channel(self._channel)


remote_control = RemoteControl()
remote_control.tune_channel(5)  # Sharp TV: выбран 5 канал
remote_control.next_channel() # Sharp TV: выбран 6 канал

Реализация паттерна Адаптер на python

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

Вырожденная форма адаптера — двухсторонний адаптер.
Нефизические примеры из жизни — это например система СМЭВ, которая предоставляет сервисы для получения/ обмена информацией.
Однако для работы вашего приложения со СМЭВ нужны адаптеры на вашей стороне, которые могут воспринимать данные, полученные со стороны СМЭВ, переводя их в объекты, понятные вашей системе.

Существуют адаптеры класса и адаптеры объекта.

пример адаптера на python для круглых отверстий и кольев квадратного сечения

# coding=utf-8
import math


class Hole(object):
    """
    Абстрактная дырка в вашем коде
    """
    def __init__(self, r):
        # задаем радиус дыры
        self.r = r

    def put(self, obj):
        # пытаемся поместить
        try:
            # чтобы влезло, нужно,
            # чтобы радиус дырки позволял
            if self.r >= obj.r:
                print u'Лезет!'
            else:
                print u'Не лезет'
        except AttributeError:
            print (u'Переданный объект не умеет вычислять радиус дырки,' 
                   u' в которую он влезет! Напишите Адаптер на python!')


class Square(object):
    """
    Абстрактный квадратный кол, который позволит
    закрыть абстрактную дырку в коде
    """
    def __init__(self, x, h):
        # зададим параметры дрына
        self.x = x
        self.h = h


class SquareHoleAdapter(object):
    def __init__(self, sq_obj):
        self.sq_obj = sq_obj

    @property
    def r(self):
        # половина диагонали квадрата будет как раз влезать
        # в дырку радиусом с полученное значение
        return math.sqrt(2*(self.sq_obj.x**2))/2


h1 = Hole(5)
h2 = Hole(2)
s1 = Square(5, 7)
s2 = Square(3, 3)
sa = SquareHoleAdapter(s2)

h1.put(s1)
>>> Переданный объект не умеет вычислять радиус дырки, в которую он влезет! Напишите Адаптер на python!

h1.put(sa)
>>> Лезет!

h2.put(sa)
>>> Не лезет