Disons que j'ai une classe Order
. Un Order
peut être terminé en appelant la méthode Order.finish()
. En interne, lorsqu'une Order
est terminée, une date d'arrivée est fixée:Comment respecter l'encapsulation lors du stockage de données dans des applications centrées sur un domaine?
Order.java
public void finish() {
finishingDate = new Date();
}
Dans la logique métier de l'application, il n'y a pas besoin d'exposer une Order
« s finishingDate
, il est un domaine privé sans getter. Imaginez qu'après avoir terminé un Order
, je veux le mettre à jour dans une base de données. Par exemple, je pourrais avoir un OTI avec une méthode update
:
OrderDao.java
public void update(Order order) {
//UPDATE FROM ORDERS SET ...
}
Dans cette méthode, je dois l'état interne du Order
, afin de mettre à jour la table des champs. Mais je l'ai déjà dit, il n'y a aucun besoin dans ma logique métier d'exposer le champ finishingDate
de Order
.
Si j'ajoute une méthode Order.getFinishingDate()
:
- Je change le contrat de
Order
classe sans ajouter de la valeur commerciale, UBT pour des raisons « techniques » (uneUPDATE
dans une base de données) - Je suis violer le principe de l'encapsulation de la programmation orientée objet, puisque j'expose l'état interne.
Comment avez-vous résolu ceci? Pensez-vous que l'ajout de getters (comme les classes "entity" dans ORM) est acceptable?
J'ai vu une approche différente où la classe elle-même (implémentation) sait même comment persister elle-même. Quelque chose comme ceci (exemple très naïve, il est juste pour la question):
public interface Order {
void finish();
boolean isFinished();
}
public class DbOrder implements Order {
private final int id;
private final Database db;
//ctor. An implementation of Database is injected
@Override
public void finish() {
db.update("ORDERS", "FINISHING_DATE", new Date(), "ID=" + id);
}
@Override
public boolean isFinished() {
Date finishingDate = db.select("ORDERS", "FINISHING_DATE", "ID=" + id);
return finishingDate != null;
}
}
public interface Database {
void update(String table, String columnName, Object newValue, String whereClause);
void select(String table, String columnName, String whereClause);
}
Outre les problèmes de performance (en fait, il peut être mis en mémoire cache ou quelque chose), j'aime cette approche, mais il nous oblige à se moquer de beaucoup de choses lors du test, puisque toute la logique n'est pas "en mémoire". Je veux dire, les données requises pour "exécuter" la logique sous test n'est pas seulement un champ en mémoire, mais il est fourni par un composant externe: dans ce cas, le Database
.
Merci d'avoir répondu à Robert. Il m'est difficile de voir la méthode 'persist()' faire partie de l'entreprise, en fait. Que pensez-vous de l'approche que je viens de poster? Vérifiez la question modifiée. Encore merci –
J'aime bien cette idée, bien qu'elle ne puisse être utilisée que si toute la logique est déléguée à la BD, ce qui n'est pas toujours le cas. Mais bien sûr, vous n'avez pas besoin d'avoir une méthode 'persist()' si l'objet sait quand le faire quand même. Par exemple, la logique 'finish()' pourrait inclure la persistance de l'état actuel dans la base de données automatiquement. Tout dépend des exigences et de la sémantique exactes. –