Цепочка обязанностей (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]

Паттерн Фабричный метод (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 канал