Посетитель (Visitor)

Посетитель (Visitor)
Поведенческий паттерн проектирвоания, который описывает операцию, выполняемую с каждым объектом из некоторой структуры.
Паттерн посетитель позволяет определить новую операцию, не изменяя классы этих объектов.
Посетитель — это Команда

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

# coding: utf-8

class FruitVisitor(object):
    """Посетитель"""
    def draw(self, fruit):
        methods = {
            Apple: self.draw_apple,
            Pear: self.draw_pear,
        }
        draw = methods.get(type(fruit), self.draw_unknown)
        draw(fruit)

    def draw_apple(self, fruit):
        print 'Яблоко'

    def draw_pear(self, fruit):
        print 'Груша'

    def draw_unknown(self, fruit):
        print 'Фрукт'


class Fruit(object):
    """Фрукты"""
    def draw(self, visitor):
        visitor.draw(self)


class Apple(Fruit):
    """Яблоко"""


class Pear(Fruit):
    """Груша"""


class Banana(Fruit):
    """Банан"""


visitor = FruitVisitor()

apple = Apple()
apple.draw(visitor)
# Яблоко

pear = Pear()
pear.draw(visitor)
# Груша

banana = Banana()
banana.draw(visitor)
# Фрукт

Посетитель (Visitor): 1 комментарий

  1. Шаблон visitor чрезвычайно полезен при работе с определенными видами информации, такими как абстрактные синтаксические деревья. Можно назвать это, версией «для бедных» языков, которые изначально не поддерживают работу с типами сумм . К сожалению, там используется перегрузка функций, чего не хватает в таких языках, как Python.
    Это сообщение в блоге Криса Лэмба представляет собой умный обходной путь, но не дает фактической реализации для соответствующих декораторов. Идея выглядит следующим образом:

    class Lion: pass
    class Tiger: pass
    class Bear: pass

    class ZooVisitor:
    @visitor(Lion)
    def visit(self, animal):
    return «Львы»

    @visitor(Tiger)
    def visit(self, animal):
    return «Тигры»

    @visitor(Bear)
    def visit(self, animal):
    return «и медведи, о боже!»

    animals = [Lion(), Tiger(), Bear()]
    visitor = ZooVisitor()
    print(‘, ‘.join(visitor.visit(animal) for animal in animals))
    # Печать «Львы, Тигры, и медведи, о боже!»

    Это выглядит слегка подозрительно (поскольку, мы определили три конфликтующих метода в одном классе), но можно постараться написать @visitor таким образом, чтобы он работал:

    # Сначала работает пара помощников

    def _qualname(obj):
    «»»Получить полное имя объекта (включая модуль).»»»
    return obj.__module__ + ‘.’ + obj.__qualname__

    def _declaring_class(obj):
    «»»Получить имя класса, который объявил объект.»»»
    name = _qualname(obj)
    return name[:name.rfind(‘.’)]

    # Сохраняет фактические методы посетителей(visitor)

    _methods = {}

    # Делегирование реализации посетителя
    def _visitor_impl(self, arg):
    «»»Фактическая реализация метода посетителя.»»»
    method = _methods[(_qualname(type(self)), type(arg))]
    return method(self, arg)

    # Фактический @visitor декоратор
    def visitor(arg_type):
    «»»Декоратор, создающий метод посетителя.»»»

    def decorator(fn):
    declaring_class = _declaring_class(fn)
    _methods[(declaring_class, arg_type)] = fn

    # Заменит все декорированные методы на _visitor_impl
    return _visitor_impl

    return decorator

    Хитрость здесь в том, что декоратор заменяет все методы visit на _visitor_impl (переопределение существующего метода в Python нормально). Но прежде чем это сделать, он сохраняет исходный метод в словаре, _methods, с ключом посетителя класса и желаемого типа аргумента. Затем при вызове метода visit _visitor_impl ищет соответствующую реализацию и вызывает ее на основе типа аргумента.

    Работает для python>=3.3

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *