Глава 12. Объектно-реляционные типовые решения... 323
Хотя детали поведения могут различаться в зависимости от выбранной схемы отобра-
жения (наследование с одной таблицей (Single Table Inheritance, 297), наследование с таб-
лицами для каждого класса (Class Table Inheritance, 305) и наследование с таблицами для
каждого конкретного класса (Concrete Table Inheritance, 313)), общая структура остается
одной и той же.
Принцип действия
Преобразователи можно организовать в иерархию, так, чтобы у каждого класса доме-
на был свой преобразователь, который будет загружать и сохранять данные этого класса.
В этом случае у нас есть одна точка, в которой можно изменить принцип отображения.
Данный подход хорошо применять для конкретных преобразователей, которые "знают",
как отображать конкретные объекты иерархии. Тем не менее иногда преобразователи
нужны и для абстрактных классов. Это можно реализовать с помощью специальных пре-
образователей, которые находятся за пределами базовой иерархии, однако делегируют
выполнение операций соответствующим конкретным преобразователям.
Чтобы попроще объяснить принцип действия преобразователей наследования, я начну
с конкретных преобразователей. В нашей модели есть конкретные преобразователи для
Объектов Footballer (футболист), Cricketer (игрок В крикет) И Bowler (игрок В бо-
улинг). Базовое поведение этих преобразователей включает в себя методы поиска, встав-
ки, обновления и удаления.
Методы поиска объявлены в конкретных производных классах, поскольку они долж-
ны возвращать экземпляр конкретного класса. Другими словами, метод класса Bowler-
Mapper должен возвращать не абстрактный объект, а объект Bowler (игрок в боулинг).
Большинство объектно-ориентированных языков программирования не позволяют из-
менять объявленный тип метода, поэтому невозможно наследовать метод поиска от абст-
рактного класса и одновременно присвоить этому методу конкретный тип. Разумеется,
можно возвращать абстрактный объект, однако это вынудит пользователя класса выпол-
нять обратное приведение объекта к нужному типу, чего следует всячески избегать.
(Данная проблема не возникает в языках с возможностью динамической типизации.)
Основное поведение метода поиска заключается в том, чтобы найти заданную строку
базы данных, создать экземпляр объекта корректного типа (это определяется производ-
ным классом) и загрузить в новый объект найденные данные. Метод загрузки реализован
в каждом преобразователе иерархии, выполняющем загрузку данных для соответствую-
щего класса. Это значит, что метод загрузки класса BowlerMapper загружает данные,
специфичные для класса Bowler, после чего вызывает метод суперкласса, чтобы тот за-
грузил данные, специфичные для класса Cricketer. Упомянутый суперкласс вызывает
метод своего суперкласса и т.д.
Методы обновления и вставки выполняются по одной и той же схеме с использовани-
ем метода сохранения. В данном случае интерфейс этих методов можно определить в су-
перклассе, а точнее, в супертипе слоя (Layer Supertype, 491). Метод вставки создает новую
строку и затем сохраняет в ней данные объекта домена, используя для этого методы со-
хранения. Метод обновления просто сохраняет данные, используя для этого все те же ме-
тоды сохранения. Последние выполняются аналогично методам загрузки: каждый класс
сохраняет специфические для него данные и вызывает метод своего суперкласса.
Данная схема значительно упрощает написание преобразователей для сохранения
информации об определенной части иерархии. Следующим шагом является реализация