Сохраняйте и загружайте объекты, не нарушая инкапсуляцию

Я хочу сохранить и загрузить объекты в базу данных без использования 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 для некоторых предопределенных условий.

Таким образом, вы можете иметь лучшую инкапсуляцию.

Я действительно не понимаю, как использование методов получения / установки нарушает инкапсуляцию. Получатели и установщики уважают инкапсуляцию. На самом деле они являются средством для достижения этого.

Другие вопросы по тегам