FabryHw
25-07-2011, 18:41
Stavo implementando un layer di accesso alla base dati usando dei DTO (anche se sarebbero più degli Entity Beans) con relative classi DAO che usano JDBC (JPA/Hibernate scartati dopo mesi di litigi e grosse difficoltà ad adattarli a DB esistenti) e mi sono venuti dei dubbi (scarsa esperienza sulla cosa) su come gestire il passaggio degli oggetti non primitivi.
Facciamo un esempio con una classe DTO che contiene 2 oggetti non primitivi:
public class MyDTO implements Serializable {
@NotNull
private Key1 pKey = new Key1();
private String description;
@NotNull
private String attribute1;
@Min(value=0)
private int quantity;
@NotNull
private Key categoryFKey = new Key2();
public MyDTO () {
}
...
}
con le altre classi così definite:
public class Key1 implements Serializable {
@Min(value=1)
private int id;
@NotNull @Size(min=1, max=2)
private String type;
public Key1 () {
}
public Key1 (int id, String type) {
this.id= id;
this.type = type;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id= id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}
}
public class Key2 implements Serializable {
@NotNull @Size(min=1, max=4)
private String category;
@NotNull @Size(min=1, max=2)
private String subCategory;
public Key2 () {
}
public Key2 (String category, String subCategory) {
this.category= category;
this.subCategory= subCategory;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category= category;
}
public String getSubCategory() {
return subCategory;
}
public void setSubCategory(String subCategory) {
this.subCategory= subCategory;
}
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}
}
Ora il mio dubbio è come devono essere i getter/setter di MyDTO quando hanno a che fare con gli oggetti non primitivi.
Ossia ritornare una copia del riferimento all'oggetto contenuto o clonare questo oggetto e ritornare il riferimento alla copia.
Codice Soluzione 1:
public Key1 getPK() {
return pKey;
}
public void setPK(Key1 pKey) {
this.pKey= pKey;
}
public Key2 getCategoryFKey() {
return categoryFKey;
}
public void setCategoryFKey(Key2 fKey) {
this.categoryFKey = fKey;
}
Codice Soluzione 2:
public Key1 getPK() {
Key1 clone = new Key1();
clone.setId(pKey.getId());
clone.setType(pKey.getType());
return clone;
//Oppure in modo meno prolisso un
// return new Key1(pKey.getId(), pKey.getType());
}
public void setPK(Key1 pKey) {
this.pKey.setId(pKey.getId());
this.pKey.setType(pKey.getType());
}
public Key2 getCategoryFKey() {
return new Key2(categoryFKey.getCategory(), categoryFKey.getSubCategory();
}
public void setCategoryFKey(Key2 fKey) {
this.categoryFKey.setCategory(fKey.getCategory());
this.categoryFKey.setSubCategory(fKey.getSubCategory());
}
Con la prima soluzione i vantaggi sono:
Codice più semplice per i getter e setter (spesso autogenerato dall'IDE)
Possibilità di condividere alcuni oggetti interni tra più oggetti
Non vengono creati oggetti a vita brevissima (necessari solo per passare i parametri)
Se cambio le definizioni di Key1 o Key2 automaticamente MyDTO è aggiornato senza dover modificare il suo codice
ma gli svantaggi sono:
Espongo lo stato interno dell'oggetto a modifiche indirette
Rischio che modifiche a nuovi oggetti indirettamente (e probabilmente erroneamente) cambino anche altri oggetti già esistenti.
Esempio
MyDTO obj1 = new MyDTO();
....
Key2 cat = obj1.getCatogoryFKey();
MyDTO obj2 = new MyDTO();
...
obj2.setCategoryFKey(cat);
...
obj2.getCategoryFKey().setCategory("0200");
obj2.getCategoryFKey().setSubCategory("IT");
ecco le ultime 2 righe volevano assegnare una nuova categoria a obj2 ma indirettamente l'hanno cambiata anche a obj1
Più facile cadere in memory leaks causa riferimenti incrociati e/o condivisi tra vari oggetti
La soluzione 2 ha come vantaggi:
Lo stato interno di ogni oggetto è modificabile solo attraverso i setter e modifiche agli oggetti esportati non cambiano lo stato interno di chi li ha esportati.
Più difficile che avvegano memory leaks
ma per contro presenta come svantaggi:
Getter e Setter più complicati da scrivere
Se cambiano Key1 e Key2 bisogna rivedere tutti i getter e setter di MyDTO coinvolti a meno di trovare una soluzione generica per il cloning.
Vengono creati tanti oggetti a vita brevissima per passare i parametri o restituire il risultato.
Maggiore lavoro per il garbage collector e maggior uso di cpu nel creare i duplicati.
Maggior uso di memoria perché non si condivide nulla (anche quando si poteva farlo in sicurezza) ed ogni oggetto interno è duplicato
Quindi qual'è l'approccio migliore ?
C'è una terza possibilità ?
Facciamo un esempio con una classe DTO che contiene 2 oggetti non primitivi:
public class MyDTO implements Serializable {
@NotNull
private Key1 pKey = new Key1();
private String description;
@NotNull
private String attribute1;
@Min(value=0)
private int quantity;
@NotNull
private Key categoryFKey = new Key2();
public MyDTO () {
}
...
}
con le altre classi così definite:
public class Key1 implements Serializable {
@Min(value=1)
private int id;
@NotNull @Size(min=1, max=2)
private String type;
public Key1 () {
}
public Key1 (int id, String type) {
this.id= id;
this.type = type;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id= id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}
}
public class Key2 implements Serializable {
@NotNull @Size(min=1, max=4)
private String category;
@NotNull @Size(min=1, max=2)
private String subCategory;
public Key2 () {
}
public Key2 (String category, String subCategory) {
this.category= category;
this.subCategory= subCategory;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category= category;
}
public String getSubCategory() {
return subCategory;
}
public void setSubCategory(String subCategory) {
this.subCategory= subCategory;
}
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}
}
Ora il mio dubbio è come devono essere i getter/setter di MyDTO quando hanno a che fare con gli oggetti non primitivi.
Ossia ritornare una copia del riferimento all'oggetto contenuto o clonare questo oggetto e ritornare il riferimento alla copia.
Codice Soluzione 1:
public Key1 getPK() {
return pKey;
}
public void setPK(Key1 pKey) {
this.pKey= pKey;
}
public Key2 getCategoryFKey() {
return categoryFKey;
}
public void setCategoryFKey(Key2 fKey) {
this.categoryFKey = fKey;
}
Codice Soluzione 2:
public Key1 getPK() {
Key1 clone = new Key1();
clone.setId(pKey.getId());
clone.setType(pKey.getType());
return clone;
//Oppure in modo meno prolisso un
// return new Key1(pKey.getId(), pKey.getType());
}
public void setPK(Key1 pKey) {
this.pKey.setId(pKey.getId());
this.pKey.setType(pKey.getType());
}
public Key2 getCategoryFKey() {
return new Key2(categoryFKey.getCategory(), categoryFKey.getSubCategory();
}
public void setCategoryFKey(Key2 fKey) {
this.categoryFKey.setCategory(fKey.getCategory());
this.categoryFKey.setSubCategory(fKey.getSubCategory());
}
Con la prima soluzione i vantaggi sono:
Codice più semplice per i getter e setter (spesso autogenerato dall'IDE)
Possibilità di condividere alcuni oggetti interni tra più oggetti
Non vengono creati oggetti a vita brevissima (necessari solo per passare i parametri)
Se cambio le definizioni di Key1 o Key2 automaticamente MyDTO è aggiornato senza dover modificare il suo codice
ma gli svantaggi sono:
Espongo lo stato interno dell'oggetto a modifiche indirette
Rischio che modifiche a nuovi oggetti indirettamente (e probabilmente erroneamente) cambino anche altri oggetti già esistenti.
Esempio
MyDTO obj1 = new MyDTO();
....
Key2 cat = obj1.getCatogoryFKey();
MyDTO obj2 = new MyDTO();
...
obj2.setCategoryFKey(cat);
...
obj2.getCategoryFKey().setCategory("0200");
obj2.getCategoryFKey().setSubCategory("IT");
ecco le ultime 2 righe volevano assegnare una nuova categoria a obj2 ma indirettamente l'hanno cambiata anche a obj1
Più facile cadere in memory leaks causa riferimenti incrociati e/o condivisi tra vari oggetti
La soluzione 2 ha come vantaggi:
Lo stato interno di ogni oggetto è modificabile solo attraverso i setter e modifiche agli oggetti esportati non cambiano lo stato interno di chi li ha esportati.
Più difficile che avvegano memory leaks
ma per contro presenta come svantaggi:
Getter e Setter più complicati da scrivere
Se cambiano Key1 e Key2 bisogna rivedere tutti i getter e setter di MyDTO coinvolti a meno di trovare una soluzione generica per il cloning.
Vengono creati tanti oggetti a vita brevissima per passare i parametri o restituire il risultato.
Maggiore lavoro per il garbage collector e maggior uso di cpu nel creare i duplicati.
Maggior uso di memoria perché non si condivide nulla (anche quando si poteva farlo in sicurezza) ed ogni oggetto interno è duplicato
Quindi qual'è l'approccio migliore ?
C'è una terza possibilità ?