Hibernate, подклассы и шаблон посетителя
Возможно, я использовал неправильное слово, поэтому, когда я говорю Business Object (BO), я имею в виду класс со ссылкой на класс, который отображается в таблицу базы данных с помощью Hibernate, а также бизнес-логику.
Проблема, с которой я сталкиваюсь, заключается в создании правильного BO для подклассов без использования отражения или instanceof.
Например, представьте, что у меня есть таблица Pen с одной ссылкой на таблицу Animal, в которой, в свою очередь, есть две вложенные таблицы Cat и Dog (все ссылки один на один). Классы выглядят примерно так:
public class Pen {
private Animal a;
// Getters and setters
}
public class Animal {
// Getters and setters
}
public class Dog extends Animal {
// Getters and setters
}
public class Cat extends Animal {
// Getters and setters
}
public class PenBO {
private Pen p;
public AnimalBO getAnimalBO() { ... }
}
public interface Action {
void visit(DogBO dbo);
void visit(CatBO cbo);
}
public class Sound implements Action {
void visit(DogBO dbo) { ... }
void visit(CatBO cbo) { ... }
}
public interface AnimalBO {
void accept(Sound s);
}
public class DogBO implements AnimalBO {
Dog d;
void accept(Sound s) {
s.visit(this);
}
}
public class CatBO implements AnimalBO {
Cat c;
void accept(Sound s) {
s.visit(this);
}
}
Затем я просто работаю с BO, создавая их таким образом в методах get этих BO:
Pen p = ... // Get or load from database
PenBO pbo = new PenBO();
pbo.setPen(p);
Затем я использую такие классы:
pbo.getAnimalBO().accept(new Sound());
Это метод getAnimalBO, над которым я работаю. Я хочу, чтобы он возвращал правильный экземпляр BO, основанный на Animal-экземпляре Pen.
Я "мог" использовать instanceof для проверки фактического Animal из текущего пера, но это явно не красиво. Другой альтернативой, о которой я думал, было использование отражения, чтобы получить имя класса и потом добавить "BO" и получить экземпляр этого, однако это также довольно уродливо.
Я попытался обернуть другой шаблон посетителя вокруг getAnimalBO, но он не может выбрать правильный метод посещения без приведения, и я не хочу добавлять accept-методы к классам не-BO.
Если нет никакого умного способа заставить этот метод работать эффективно, что-то не так в ядре? Я действительно не нашел лучших практик для Hibernate. Некоторые примеры Hibernate и шаблона посетителя просто добавляют методы accept к сопоставленным классам, что не может быть хорошим...
1 ответ
Ваша проблема на самом деле является просто проблемой ОО с наследованием и преобразованием из одной иерархии классов в параллельную. Вы уже решили моделирование базы данных этого и отображения Hibernate, и в вашей реализации посетителя, очевидно, нет ничего плохого. Просто вы не хотите применять шаблон посетителя к отображенной иерархии классов, и поэтому вы создали себе проблему при преобразовании между двумя иерархиями.
В настоящее время часть вашего кода будет нести ответственность за знание того, что CatBO
соответствует Cat
и т. д., а также для того, чтобы знать, что если Pen
объекты Animal
собственность на самом деле Cat
, то содержащий PenBO
объекты AnimalBO
собственность на самом деле будет CatBO
, Учитывая это, он должен быть в состоянии определить тип Animal
экземпляр возвращен, поэтому я не вижу, как можно избежать использования одного из instanceof
или отражение. Что вы возражаете против instanceof
? Отражение, чтобы получить имя класса и добавление "BO", очень противно и некрасиво, я согласен, хотя это, очевидно, может быть сделано для работы. Но единственный метод, ответственность которого состоит в том, чтобы взять Animal
и вернуть подходящий AnimalBO
используя instanceof
и выбрав правильный BO
класс не плохая вещь в данных обстоятельствах ИМХО.
Вопрос, который я хотел бы задать, заключается в том, почему вы вообще так отображаете иерархию классов. Вы сказали, что не хотите добавлять accept
методы к отображенным классам, и что это "не может быть хорошо", но вы еще не объяснили, почему вы так чувствуете. Это хуже, чем варианты, которые вы уже представили? Добавляя шаблон посетителя в сопоставленную иерархию классов, вы на самом деле не внедряете в него какую-либо бизнес-логику, вы просто разрешаете применение бизнес-логики по всей иерархии с использованием шаблона посетителя. Таким образом, ваш уровень бизнес-логики содержит простые, общие реализации посетителя, ваш сопоставленный класс или уровень домена - это POJO плюс базовая реализация посетителя, и у вас действительно есть хорошее чистое разделение, которое, я думаю, вам нужно.