Сохраняйте и загружайте объекты, не нарушая инкапсуляцию
Я хочу сохранить и загрузить объекты в базу данных без использования ORM (например, Hibernate).
Допустим, у меня есть следующий класс:
public class Person {
private int age;
public void birthday(){
age++;
}
}
Этот класс не предоставляет метод get, поэтому внутреннее состояние (age) инкапсулировано.
Если я хочу сохранить объект, мне нужен метод получения, чтобы сделать следующее:
insert into TABLE_PERSON (id, age) vales (1, person.getAge());
В противном случае мне нужен метод установки для загрузки объекта:
int age = "Select age FROM Person";
person.setAge(age);
Но я не хочу нарушать инкапсуляцию (реализуя дополнительные методы setter и getter) только для сохранения и загрузки объектов.
Есть ли возможность сделать это? Может быть, какой-то шаблон (сувенир?) Или лучшая практика?
Заранее спасибо!
5 ответов
Вы упомянули Memento. Существует вариант под названием Snapshot, задокументированный Grand. Вот диаграмма классов UML:
Person
будет Originator
в шаблоне.
Вы можете сбросить экземпляр Memento в двоичный объект в базе данных.
Обратите внимание, что Memento - это внутренний класс Originator. CreateMemento / setMemento - это разновидность get / set, которая может нарушить инкапсуляцию, если вы пурист. Однако пакет информации (Memento), используемый при вызове, является интерфейсом без методов, поэтому инкапсуляция Originator's
состояние гарантировано. Это может даже работать с ORM, если вы правильно отобразите его.
Конечно, это кажется большой работой, чтобы избежать получения / установки. Ваш пример личности / возраста не совсем реалистичен (не очень хорошая модель человека). Вполне нормально выставить возраст или дату рождения человека, и выставить это свойство для сохранения объекта будет в порядке. Инкапсуляция не значит ничего не раскрывать. Это означает, что не раскрывайте слишком много деталей.
Например, давайте не будем использовать age
скорее Person.birthday
, Это может быть сохранено внутри как строка в формате ГГГГ-ММ-ДД или с использованием объекта Date. Такая деталь не будет выставлена (будет заключена в капсулу). Это также сделано для того, чтобы вы могли изменить его и не влиять на клиентов вашего класса. Все, что вы скрываете, может быть изменено без негативного влияния на клиентов.
Объявляя день рождения человека, вы говорите: "Я рискую, что у Человека всегда будет день рождения". Эта часть раскрыта и, следовательно, будет трудно изменить, не нарушая клиентов.
Редактировать после комментариев
Публичные методы (например, save
а также load
) в Person
может позаботиться о сохранении / загрузке базы данных. У них будет доступ к приватным полям (age
) и может получить работу. Вы сказали, что не используете ORM, поэтому вы просто делаете это сами.
Аллен Голуб написал несколько статей именно на эту тему. Он предлагает Строителя и то, что он называет шаблоном Обратного Строителя.
То, как это будет работать в вашем случае, Человек будет нести ответственность за создание представления себя. То есть вы определяете интерфейсы PersonImporter и PersonExporter для перемещения данных в объект Person и из него. Эти классы в основном являются частью дизайна Person. Так:
interface PersonImporter {
public int getAge();
public String getId();
}
interface PersonExporter {
public void setDetails(String id, int age);
}
class Person {
private int age;
private String id;
public Person(PersonImporter importer) {
age = importer.getAge();
id = importer.getId();
}
public void export(PersonExporter exporter) {
exporter.setDetails(id, age);
}
}
Это не устраняет полностью геттеры и сеттеры, а контролирует их с помощью интерфейсов.
Одним из способов здесь является использование классов Mapper. Создайте PersonMAP, который отображает класс в таблицу и инкапсулирует все операции базы данных в этом классе.
Это то, что я думаю, не может быть идеальным.
Инкапсуляция делается для сокрытия данных. Возможно, вы просто не хотите, чтобы кто-то установил неправильное значение age
приписывать.
Может быть, вы можете ввести класс-обертку, который используется внешним кодом, и только этот класс может использовать ваш Person
учебный класс.
Если вы создаете файл с public
класс-оболочка скажем PersonWrapper
, что обеспечивает геттер и сеттер для возраста. Но эти методы получения и установки могут иметь желаемую логику проверок, например, какие значения могут быть установлены для age
кто может и т. д. в одном файле, Person
класс может быть определен как private
но с простым геттером и сеттерами для age
пары. Ваш PersonWrapper
следует использовать только Person
класс getter и setter для некоторых предопределенных условий.
Таким образом, вы можете иметь лучшую инкапсуляцию.
Я действительно не понимаю, как использование методов получения / установки нарушает инкапсуляцию. Получатели и установщики уважают инкапсуляцию. На самом деле они являются средством для достижения этого.