Всем привет :)
Столкнулся с вопросом, наверно глупым.
Есть у меня класс User, делаю я его так, что бы он сам мог сохранятся и удалятся и т.п. в БД. То есть, реализую в нем паблик методы User.save()
и User.delete()
etc., которые просто пердают себя в DAO. new JDBCUserDAO().insert(this);
Но задумался, что идет просто проброска и лишний код, а не лучше ли иметь почти пустой класс User и просто кидать его по другим объектам, которые его и будут сохранять или обрабатывать. Допустим имея некий объект класса User, для его сохранения передать его в DAO new JDBCUserDAO().insert(user);
Я предполагаю, что можно и так и так. Но текущей вариант(первый), вроде бы как, соответствует понятию инкапсуляция, но создается лишний код.
public class User {
private String name;
private String mail;
public User(String name) {
DAO<User> userBD=new JDBCUserDAO();
User user=userBD.takeByName(name);
this.name = name;
this.mail = user.getMail;
}
public User(String name, String mail){
if (!isNameOK(name)) {throw new WrongArgsExeption("Name filed="+name);} else {this.name = name;}
if (!isMailOK(mail)) {throw new WrongArgsExeption("Mail filed="+mail);} else {this.mail = mail;}
}
public void save() throws WrongArgsExeption {
DAO<User> userBD=new JDBCUserDAO();
if (this.isInBd()) {throw new WrongArgsExeption("This Name or Mail already used.");}
userBD.insert(this);
}
public void update() {
DAO<User> userBD=new JDBCUserDAO();
userBD.update(this);
}
public String getName() {
return name;
}
public String getMail() {
return mail;
}
}
Простите за кашу в голове, я только учусь :)
Инкапсуляция и сокрытие данных — близкие понятия, но не идентичные.
Инкапсуляция означает, что ваш класс нельзя сломать, перевести в несогласованное состояние. Скажем, если вы пишете класс Data
, и позволяете программисту устанавливать значения полей Day
и Month
, он может установить несуществующую дату 30 февраля.
Чтобы избежать таких ситуаций, классы позволяют изменять значения связанных полей только вместе. Скажем, в Data
можно завести метод setData(int year, int month, int day)
. Если вы передаёте на вход неправильную дату, метод выбрасывает исключение и не меняет состояние объекта на неправильное.
Это и есть инкапсуляция.
Ваше решение никак не соответствует инкапсуляции. Возможно, оно соответствует сокрытию? Иногда под сокрытием понимают такую ситуацию, что про класс вообще ничего не известно.
Но это не совсем правильно. Представим себе класс Line
который представляет математическую линию.
Линию можно создать, используя общее уравнение прямой Ax + By + C = 0, либо, например, координатами двух точек, через которые она проходит.
public Line(double a, double b, double c) { ... }
public Line(double x1, double y1, double x2, double y2) { ... }
Предположим, мы хотим узнать пересекается ли наша прямая и окружность Circle
? Код пересечение кажется не принадлежит классу Line
и классу Circle
, он касается их обоих.
Третий класс, какой-нибудь сервис пересечений, мог бы заниматься решением этой задачи. Для решения ему нужны коэффициенты A, B и C общего уравнения.
Класс Line
должен их предоставить. Но как? Если мы откроем доступ к этим коэффициентам, их можно будет несогласованно менять. Вместо предоставления прямого доступа, мы закрываем доступ методами-геттерами getA()
, getB()
и getC()
.
Точно также для решения других задач мы предоставляем геттеры для x1, y1, x2, y2. Значит ли это, что Line
хранит все возможные способы представления прямой? Возможно. Или возможно, он вычисляет их на лету. Тогда какие параметры хранятся там на самом деле?
Мы не знаем, и не должны. Для решения наших задач нам эта информация не нужна. Это сокрытие. В каком то смысле ваше решение способствует именно сокрытию реализации.
Но здесь есть один подводный камень, и он большой. Класс User
отражает сущность предметной области. Это пользователь, наверное, у него есть имя и, возможно, какие то права доступа.
А вот то, что относится к хранению пользователя — лишняя информация для предметной области. Ваш класс знает о базе данных, и о том, как он хранится. Потом вам потребуется кэшировать записи, и придётся добавлять кэш туда же. Потом для отображения пользователя на сайте, нужно будет конвертировать его в JSON.
Если идти по этому пути, классы быстро обрастут ненужной функциональностью. Они станут огромными и их трудно будет поддерживать. Всемогущие классы иногда называют антипаттернами Божественный объект. Божественный, потому что может всё.
Следуя принципу Единственной ответственности (Single Responcibility Principle), мы отделяем предметную логику от логики хранения и логики представления. Тогда у нас получается простой класс User
, плюс простой класс UserRepository
— который сохраняет и загружает пользователей в базу и из базы, плюс простой класс JsonSerializer
.
Что хорошего в вашем решении
Не смотря на нарушение принципа единственной ответственности, иногда программисты делают так, как сделали вы. Ваше решение это паттерн Активный объект (Active Object), то есть такой объект, который что-то делает в предметной области, а ещё умеет себя сохранять и загружать.
Если предметная логика у вас небольшая, если в объектах почти нет методов, а только данные, решение, основанное на Активных объектах и в самом деле проще и быстрее.
Поэтому, прежде чем всё переписывать, оцените, будет ли значимая отдача. Небольшие активные объекты — приемлемое решение, надо просто оценить требования.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Какие существуют виды рекламных бордов и как выбрать подходящий?
Честно гуглилНашел это: OutputStreamWriter vs FileWriter
Имеется map с ценами <наименование, цена> и map с заказом <наименование, количество>Нужно рассчитать общую стоимость заказа
Есть 3 секции, я всегда задавал 1 div который покрывал 3 секции и задавал ему 1 бекграунд, правильной ли такой подход?