Одной из проблем использования hibernate является то, что управляемые классы должны иметь конструктор по умолчанию . Проблема в том, что нет явной точки, где класс инициализируется и инварианты могут быть проверены.
Если у класса есть инварианты, которые зависят от нескольких свойств, дизайн класса становится сложным. Давайте начнем с гипотетического дизайна зеленого поля:
public class A {
private int x;
private int y;
public A(int x, int y) {
this.x = x;
this.y = y;
checkInvariants(this.x, this.y);
}
private void checkInvariants(int x, int y) {
if (x + y « 0) throw new IllegalArgumentException();
}
}
Это базовая реализация, которая не соответствует требованиям гибернации. Инвариант проверяется в конструкторе. (Содержание метода checkInvariants () не имеет значения, он представлен только для иллюстрации того, что инварианты класса могут зависеть от нескольких свойств.)
Класс можно использовать следующим образом:
new A(0, 0);
new A(-1, 0); //invalid
Чтобы удовлетворить требования гибернации, одним из обходных путей является добавление частного конструктора по умолчанию и , использующего доступ к полю . (Я опустил отображения спящего режима.)
public class H {
int x;
int y;
public H(int x, int y) {
this.x = x;
this.y = y;
checkInvariants(this.x, this.y);
}
H(){}
private void checkInvariants(int x, int y) {
if (x + y « 0) throw new IllegalArgumentException();
}
}
Это имеет два основных недостатка:
* Вы начинаете реализовывать код, который зависит от клиента (Hibernate). В идеале класс не знает своих вызывающих.
* Особая проблема с этим обходным решением заключается в том, что экземпляры, инициированные hibernate, не проверяются , если встречаются инварианты Вы доверяете данным, загруженным из базы данных, что проблематично. Даже если ваше приложение является единственным, использующим эту конкретную схему базы данных, всегда есть возможность специальных изменений со стороны администраторов.
Второй обходной путь - проверка инвариантов в коде пользователя :
public class I {
private int x;
private int y;
public I() {}
public void checkInvariants() {
if (x + y « 0) throw new IllegalArgumentException();
}
public void setX(int x){
this.x = x;
}
public void setY(int y){
this.y = y;
}
}
I i = new I();
i.setX(-1);
i.setY(0);
i.checkInvariants();
Очевидно, это делает код пользователя более сложным и подверженным ошибкам . Этот дизайн не соответствует ожиданиям того, что экземпляр является согласованным после создания и остается согласованным после каждого изменения состояния (вызова метода). Каждый пользователь должен проверять инварианты для каждого создаваемого им экземпляра (возможно, косвенно с помощью hibernate).
Есть ли лучшее решение этой проблемы:
- не слишком сложный
- без явных знаний о его пользователях
- без зависимости от каркаса гибернации?
Я полагаю, что некоторые ограничения необходимо ослабить, чтобы найти прагматичное решение. Единственным жестким ограничением является отсутствие зависимости от каркаса гибернации. (Специальный код Hibernate вне доменных объектов в порядке).
(Просто из любопытства: есть ли платформа ORM, которая поддерживает «внедрение конструктора»?)