М.Фаулер Шаблоны корпоративных приложений.

Краткие тезисы книги М.Фаулера Шаблоны корпоративных приложений

Три слоя

Представление — отображение данных, обработка пользовательских событий, запросов

Домен — Бизнес-логика приложения

Источник данных — управление БД и пр.

 

Бизнес-логика

Сценарий транзакции (Transaction Script)

стр. 133

Организует бизнес-логику в процедуры, которые управляют каждая своим запросом. В некоторых случаях это может быть просто вывод данных из БД. В других случаях эти действия могут содержать в себе множество вычислений и проверок. Но для каждого пользовательского действия должна быть определена единая процедура, содержащая необходимые действия.

Модель предметной области (Domain Model)

стр. 140

Cеть взаимосвязанных объектов, в которой каждый объект представляет собой отдельную значащую сущность: может быть настолько большую, как корпорация или настолько малую, как строка из формы заказа.

Модуль таблицы  (Table Module)

стр. 148

Очень напоминает DomainModel. Однако, если в Domain Model каждый объект сущности соответствует одному объекту класса, то в данном паттерне 1 объект управляет всеми объектами представляемой сущности.

Слой служб (Service Layer)

стр. 156

Инкапсулирует бизнес-логику приложения, предоставляя интерфейс, который принимает на себя взаимодействие с «внешним миром»

Источники данных

Шлюз таблицы данных (TableDataGateway)

стр. 167

Представляет собой объект-прослойку между таблицей и приложением. В нем реализованы методы, которые позволяют выполнять операции над таблицей, не вдаваясь в подробности SQL-команд.

Шлюз записи данных (RowDataGateway)

стр. 175

см. Шлюз таблицы данных, только для 1 строки

Активная запись (ActiveRecord)

стр. 182

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

Преобразователь данных (DataMapper)

стр. 187

Паттерн разделяющий объект и БД, служащий прослойкой между ними, направлен на исключение из класса объекта кода по работе с БД.

Моделирование поведения

Единица работы (Unit Of Work)

стр. 205

По сути представляет собой объект работы транзакции. Отслеживает изменения в БД.

Коллекция объектов (Idenity Map)

стр. 216

хранит записи об объектах, которые были извлечены из БД. при запросе, сначала ищет объект в коллекции, а затем идет в БД.

Загрузка по требованию (LazyLoad)

стр. 222

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

  • Lazy Initialization (Ленивая Инициализация) использует специальный макер (обычно null), чтобы пометить поле, как не загруженное. При каждом обращении к полю проверяется значение маркера и, если значение поля не загружено — оно загружается.
  • Virtual Proxy (Виртуальный Прокси) — объект с таким же интерфейсом, как и настоящий объект. При первом обращении к методу объекта, виртуальный прокси загружает настоящий объект и перенаправляет выполнение.
  • Value Holder (Контейнер значения) — объект с методом getValue. Клиент вызывает метод getValue, чтобы получить реальный объект. getValue вызывает загрузку.
  • Ghost (Призрак) — объект без каких-либо данных. При первом обращении к его методу, призрак загружает все данные сразу.

Предоставление данных в WEB

Модель — представление — контроллер (MVC)

стр. 347

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

Контроллер страниц (Page Controller)

стр. 350

паттерн, предполагающий наличие контроллера для каждой отдельно взятой web-страницы. анализирует url, извлекает данные из модели, определяет представление, которое должно быть предоставлено пользователю.

Контроллер запросов (Front Controller)

стр. 362

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

Представление по Шаблону (Template View)

стр. 368

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

Представление с преобразованием (Transform View)

стр. 379

представление данных, полученных из модели в пригодном для отображения на странице виде.

Двухэтапное представление (Two step view)

стр. 383

получает данные от модели в некую логическую структуру, которая затем проходит второй этап и встраивается в страницу.

Контроллер приложения (Application Controller)

cтр. 397

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

Распределенная обработка данных

Интерфейс удаленного доступа (Remote Facade)

стр. 405

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

Объект переноса данных (Data Transfer Object)

стр. 419

Позволяет за один запрос-ответ получить данные например по нескольким объектам, также экономя ресурсы на обращение к удаленному интерфейсу. В ответ мы получаем сериализованный объект, например по формату json, xml.

Базовые типовые решения

Шлюз (GateWay)

стр. 483

Объект, который инкапсулирует доступ к внешней системе или ресурсу.

Преобразователь (Mapper)

стр. 489

Объект, который управляет сообщением между независимыми друг от друга объектами.

Супертип слоя (Layer Supertype)

стр. 491

Это суперкласс для всех объектов слоя, объединяющий основные функции.

Отдельный интерфейс (Separated Interface)

стр. 492

Выделение какого-либо интерфейса к объекту в отдельный от объекта пакет. Обычно нужно для реализации логики, противоречащей концепции.

Реестр (Registry)

стр. 495

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

Объект-значение (Value Object)

стр. 500

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

Деньги (Money)

стр. 502

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

Частный случай (Special Case)

стр. 511

Практика вместо null значений возвращать объект класса с тем же интерфейсом, чтобы сократить ситуации с вызовом метода от null.

Дополнительный модуль (Plugins)

стр. 514

Связывает классы на основе конфигурации, а не явного указания в коде.

Фиктивная служба (Service stub)

стр. 519

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

Множество записей (Record Set)

стр. 523

Предоставляет объект соответствующий результату запроса к таблице или записи, позволяющий добавлять бизнес-логику.

 

 

 

 

Посетитель (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)
# Фрукт

Шаблонный метод (Template Method)

Шаблонный метод (Template Method)

Поведенческий паттерн проектирования, который позволяет подклассам расширять шаги алгоритма, не меняя при этом структуру, объявленную в базовом классе.
Также можно рассматривать как частный случай паттерна Фабричный метод
реализация паттерна проектирвоания Шаблонный метод на python

# coding: utf-8

class ExampleBase(object):
    def template_method(self):
        self.step_one()
        self.step_two()
        self.step_three()

    def step_one(self):
        raise NotImplementedError()

    def step_two(self):
        raise NotImplementedError()

    def step_three(self):
        raise NotImplementedError()


class Example(ExampleBase):
    def step_one(self):
        print 'Первый шаг алгоритма'

    def step_two(self):
        print 'Второй шаг алгоритма'

    def step_three(self):
        print 'Третий шаг алгоритма'


example = Example()
example.template_method()

# Первый шаг алгоритма
# Второй шаг алгоритма
# Третий шаг алгоритма

Снимок (Memento)

Снимок (Memento)

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

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

# coding: utf-8

class Memento(object):
    """Хранитель"""
    def __init__(self, state):
        self._state = state

    def get_state(self):
        return self._state


class Caretaker(object):
    """Опекун"""
    def __init__(self):
        self._memento = None

    def get_memento(self):
        return self._memento

    def set_memento(self, memento):
        self._memento = memento


class Originator(object):
    """Создатель"""
    def __init__(self):
        self._state = None

    def set_state(self, state):
        self._state = state

    def get_state(self):
        return self._state

    def save_state(self):
        return Memento(self._state)

    def restore_state(self, memento):
        self._state = memento.get_state()


originator = Originator()
caretaker = Caretaker()

originator.set_state('on')
print 'Originator state:', originator.get_state()  # Originator state: on
caretaker.set_memento(originator.save_state())

originator.set_state('off')
print 'Originator change state:', originator.get_state()  # Originator change state: off

originator.restore_state(caretaker.get_memento())
print 'Originator restore state:', originator.get_state()  # Originator restore state: on

Наблюдатель (Издатель-Подписчик, Слушатель, Observer)

Наблюдатель (Observer)
Поведенческий паттерн проектирования, позволяющий подписчику отслеживать изменения издателя.

Пример реализации паттерна Наблюдатель на python

# coding: utf-8

class Subject(object):
    """Субъект"""
    def __init__(self):
        self._data = None
        self._observers = set()

    def attach(self, observer):
        # подписаться на оповещение
        if not isinstance(observer, ObserverBase):
            raise TypeError()
        self._observers.add(observer)

    def detach(self, observer):
        # отписаться от оповещения
        self._observers.remove(observer)

    def get_data(self):
        return self._data

    def set_data(self, data):
        self._data = data
        self.notify(data)

    def notify(self, data):
        # уведомить всех наблюдателей о событии
        for observer in self._observers:
            observer.update(data)


class ObserverBase(object):
    """Абстрактный наблюдатель"""
    def update(self, data):
        raise NotImplementedError()


class Observer(ObserverBase):
    """Наблюдатель"""
    def __init__(self, name):
        self._name = name

    def update(self, data):
        print '%s: %s' % (self._name, data)


subject = Subject()
subject.attach(Observer('Наблюдатель 1'))
subject.attach(Observer('Наблюдатель 2'))
subject.set_data('данные для наблюдателя')
# Наблюдатель 2: данные для наблюдателя
# Наблюдатель 1: данные для наблюдателя

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

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

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

Команда

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

Посредник

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

Наблюдатель

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

Посредник (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() # Выключить свет