пятница, 2 октября 2009 г.

Detached Template Method pattern

Идея не нова, ее я встретил впервые в Spring framework, в части интеграции с ORM framework’ами. Так для упрощения написания своих Repository или DAO (Как назвать дело вкуса. Я предпочитаю термин Repository, т.к. с точки зрения DDD он более точно описывает свое назначение.) Spring предлагает 2 опции:

  • передать своему DAO XXXTemplate и пользоваться предоставляемыми им методами, инкапсулирующими последовательность работы с конкретным ORM;
  • Унаследоваться от XXXDaoSupport, в котором XXXTemplate будет подставлен без вашего участия IoC контейнером;
Так что же это такое? Давайте обратимся к классикам [GoF]:

    «Template Method – паттерн поведения [классов]. Template Method определяет основу алгоритма и позволяет подклассам переопределить некоторые шаги алгоритма, не изменяя его структуру в целом.»

Почему Detached? Ну, вот так я придумал. Потому что это не предполагающая наследования реализация идеи Template Method в отдельном классе. Методы класса определяют последовательность действий, которые должны быть выполнены, скрывая детали реализации алгоритма и нижележащих ресурсов. Переопределение же некоторых шагов возможно при помощи callback’ов.

К примеру, под капотом у всех XXXTemplate в Spring происходит следующее (упрощенно):

  1. выделение ресурсов (создание connection и т.д.); (фиксированная часть алгоритма)
  2. начало транзакции; (фиксированная часть алгоритма)
  3. вызов callback executeInTransaction; (изменяемая часть алгоритма)
  4. возврат данных; (изменяемая часть алгоритма)
  5. подтверждение/откат транзакции; (фиксированная часть алгоритма)
  6. освобождение ресурсов; (фиксированная часть алгоритма)

Все эти шаги вам пришлось бы реализовывать самим в каждом месте, где предполагался бы код доступа к данным. Кроме того XXXTemplate скрывают детали реализации и отвязывают от лишних зависимостей. Например, HibernateTemplate конвертирует hibernate-специфичные exception’ы в Spring DataAccessException.

Я использовал этот прием для обхода ограничения языка C# - запрета множественного наследования. Представьте иерархию классов, в которой некоторые классы наследники базового класса должны обладать еще и дополнительными данными/поведением. А чтобы усложнить ситуацию, представьте, что в системе есть еще классы, не принадлежащие этой иерархии, но которые тоже должны обладать теми же данными/поведением. Интерфейс тут единственное возможное решение. Теперь более конкретно, чтобы обосновать применимость.

У меня есть базовый класс Resource, реализующий disposable pattern. Многие из его наследников – объекты системы, хранящие данные о состоянии системы и идентифицирующие конкретные экземпляры по Id.

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

Как видите, IIdentifiable наследует интерфейс IEquatable, т.е. объекты должны сами определять является ли переданным им экземпляр идентичным. И, наконец, тождественность объектов должна определяться не только на основании Id, но и на основании других полей данных. Чувствуете к чему это идет? Мне придется реализовывать логику сравнения по Id и по значимым полям в каждом классе, многократно дублируя код! Я такую личную неприязнь к дублированию чувствую, что аж кушать не могу.

Поэтому давайте разберемся а что же я должен сделать? Я должен реализовать алгоритм установления тождественности, который содержит фиксированную часть (сравнение ссылок

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

Потому в IIdentifiable я ввел метод DataEquals – это изменяемая часть алгоритма и, как мне кажется наименьшее из зол (но все-таки зло, потому что он публичный и засоряет интерфейс (один из способов уменьшения этого зла - explicit implementation)), а в каждом реализуемом методе Equals интерфейса IEquatable я делегирую выполнение алгоритма моей реализации Detached Template Metod – EqualsTemplate.

Я знаю, знаю, что в .Net есть IEqualityComparer, но мне мое решение нравится больше по 3м причинам:

  1. мне не придется «заставлять» пользователей моих классов использовать IEqualityComparer;
  2. мне кажется, что реализовать один метод проще, чем написать новый класс;
  3. оно мое;
Посмотрите как просто реализуется теперь этот алгоритм:



Удачи!