DTO Design Pattern May 19, 2010 19:35 over 2 years ago
En bog er for 99 procent vedkommende bygget over den skabelon som Johann Gutenberg fastlage det i det herrens år 1455. Der findes en punktvis oversigt i starten af bogen og et index til opslag bag. Men Tor Nørretranders bog, “Det perfekte menneske” er opbygget som et anti pattern. Alle venstre siderne er udført som index mens alle højre sider er brødtekst. Det betyder at det nærmest er umuligt at navigerer eller at bruge bogen som opslagsværk. Betydningen af et så meget brugt mønster er ikke til at tage fejl af. Underligt er det at tænke på hvor børn første gang får in-prentet dette mønstre så de kan bruge biblioteket uden særlig instruktion.
Det hele startede i de glade dage med EJB’er og det faktum at man ikke kunne få data ud af ejb uden at mappe data over i et value objekt. Nye EJB’er kom til og også andre ORM mapping muligheder der understøtter en “detach” strategi. Nogle år efter kom J2EE Core Patterns bogen med nye design mønstre der var mere objekt graf orienteret DTO’er. Data Transfer Objects findes i mange arter og former og jeg har lavet mange af dem, og sagt undskyld for nogle få også.
Data transfer objects må ikke forveksles med domain objects. Domain objects indkapsler instans og adfærd. En ordre ved hvordan man løber gennem de orderlinjer. En DTO derimod bære kun data.
Transfer objects kan dog stadig bidrage med gode egenskaber alligevel. Fx kan man forstille sig at udnytte en DTO til til at flytte idempotent data eller sikre at alle værdier kun er repræsenteret en gang og i en vis orden. Indenfor softwareudvikling og navlig ved applikationer med lag inddelt arkitektur er der brug for dataholdere der kan transportere data vertikalt gennem de logiske lag.
At konstruere en DTO betyder at bygge en data struktur eller et namespace der kan holde elementer af data. Det lyder jo helt vildt gammeldags og det er det også. Hvis man dykker ned under den umiddelbare kode som programmøren arbejder med sker der ikke andet end der flyttes rundt med nogle hukommelses referencer der peger på det sted i hukommelse hvor data blive sat ind.
Det betyder at man i en DTO kan flytte rundt på en samling af pegepinde der peger på de oprindelige hukommelses data. Når man flytter en DTO vertikalt i en applikation er det stadig kunne en hukommelses adresse og derfor går det hurtigt. Præsis i dag vil jeg ikke stille spørgsmålet hvorfor så utroligt mange gider forsætte med at bruge et paragdime der helt klart er et Anti Pattern(almindeligt kendt som en dårlig løsning).

Interface DTO er den generelle super type for alle data transfer objects. Der er nogle få klasser som ikke implementere dette interface direkte men indirekte gennem den abstrakte klasse DataTransferObject.
| DTO | Generic type |
| DataTransferObject | Abstract |
| DTOCollection | An collection |
| DTOImmutableCollection | An immutable collection |
| DTOCachedHashMap | Ffixed size cache |
| DTODomain | Domain objects |
| DTOCustom | Custom domain objects |
| DTOGeneric | Generic types |
| DTOHashmap | Key value pairs |
| DTOPropertyChangeHashMap | Poperty event support |
| DTORowset | Complex detached rowset |
| DTOTypedHashmap | Enforce originally inserted type and key are allowed |
| TransferObjectFactory | Instans control |
| DefaultVO | CRUD |
| ValueObject | Value object |
| Validate, UUIDBean, HexFormat | Helpers |
Disse data objekter er delt op i tre hovedgrupper.
- Domain based data transfer objects
- Capability based data transfer objects
- Helpers & tools
Den første er den gruppe som er specialiseringer af DataTransferObject. De er alle af typen der står beskrevet i fx “Patterns of Enterprise Application Architecture” af Martin Fowler hvori der kan være visse domain sammenhænge som fx identifikation og andet. De handler alle om at transportere domain information rundt i systemet, enten i hele grafer eller i sub domains.
DTOTypedHashmap kan fryse både key og values så det lader sig gøre at sende data rundt i et system uden af en trediepart kan ændre data eller sikre at kun værdier med en særlig nøgle kan sættes.
Den anden gruppe af DTO specialiseringer er af mere forskellig observans og tilbyder forskellige egenskaber som fx. caching, not mutable objects og change aware objects. Det er den sjove gruppe.
DTOPropertyChangeHashMap
Kan man bruge hvis der er det er nødvendig med en notifikation hvergang en værdi opdateres. Objektet modtager en besked med et key:value pair. Hvis værdien ændres i mappen kaldes metoden PropertyChange() med et event. Det er perfekt til mange asynkrone UI fremworks eller hvor man typisk kommer til at sammenligne alt for mange objekt grafer.
public class DTOPropertyChangeHashMap extends HashMap implements PropertyChangeListener, DTO {
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
public DTOPropertyChangeHashMap() {
propertyChangeSupport.addPropertyChangeListener(this);
}
@Override
public Object put(Object key, Object element) {
propertyChangeSupport.firePropertyChange("" + key, get(key), element);
return super.put(key, element);
}
DTOMutableCollection
Dette objekt bruges hvis data skal bruges i en anden kontekst og man ønsker sikkerhed for at indhold ikke bliver overskrevet. Tricket består i at instanstiere en inner klasser i et interface.
public interface DTOCollection extends DTO {
public class DTOMutableCollection extends ArrayList implements DTOCollection {
}
}
Interfacet definere metoder i følge kontrakten mens den konkrete instans implementere service metoder. De kan testes sådan:
DTOImmutableCollection.DTOMutableCollection mutable = new DTOImmutableCollection.DTOMutableCollection();
mutable.add(new Integer(222));
mutable.add("A mutable collection");
DTOImmutableCollection immutable = (DTOImmutableCollection) mutable;
immutable.add (new Integer(888)); // Can't compile !!
ArrayList listOfIntegers = (ArrayList) immutable.toCollection(Integer.class);
ArrayList listOfStrings = (ArrayList) immutable.toCollection(String.class);
Den interessante linje er den med castet hvor der skiftes mellem den konkrete inder klasser til det yder interface. Interfacet holder kontakten mens inder klassen implementer kontrakten og overlader resten til den normale ArrayList.
Det betyder at man kan caste fra den konkrete klasser til det yder interface for at udnytte en begrænset kontrakt. I dette tilfælde scoper vi til en kontrakt uden mulighed for at bruge add() for at påføre mere information på objektet.
DTOCachedHashMap
Denne løsning er ikke svær men den kan løses mere eller mindre elegant. Hvis man vil gemme nogle objekter i en applikationscache har man samtidigt fået mindst to andre problemer. For det første fylder data i memory og for det andet skal data ha en TTL.
Ud af boxen styre kan dette objekt selv sin max størrelse og der kan nemt indbygges en timer. Listen styres efter FIFO princip og man skal beslutte max size samt tilføje en privat metod. Når listen er fuld og der kommer et nyt element vil det ældste i listen blive skubbet ud. Så her er det om at regne lidt på tilvæksten af objekter og hvor langt tid man har brug for dem.
public class DTOCachedHashMap extends LinkedHashMap implements DTO {
private static final long serialVersionUID = 2534279383490620665L;
/** The MAX size for entries */
private static final int MAX_ENTRIES = 10;
/** FIFO style. If this return true the first element vill be taken away so we can maintain a fix size */
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_ENTRIES;
}
}
Denne løsning er altså god at lege med når et system har mange peeks og som samtidig cacher data. Ofte vil denne form for cache være det eneste konstante i en applikation under heavy load.
En kort kommentar til value objects eller data transfer objects og hvorfor denne løsning er gået hen og blevet et antipattern for andre end legacy programører. Tja mest fordi andre og nyere teknologier som groovy og ruby er kommet på banen med ægte properties og Uniform Access Principle og ønsket om at være DRY, samt annotations som fx @Delegate og @Immutable hvilket gør ideen om at bygge særlige transport objekter til data unødvendig.
Check fx objectify
By Frank Vilhelmsen - 2 tags: java pattern - 1 comment on DTO Design Pattern - Add comment
Erik May 24, 2010 20:12 about 1 year ago
Godt indlæg, men husk…
DTO er jo bare en projektion eller et view, som er tilpasset et bestemt anvendelsesmønster. Anvendelse af DTO kan gøre det lettere for klienter at anvende et interface. Med ‘lettere’ kan både forståes programmeringsmæssigt og/eller performancemæssigt.
DTO er dermed ikke et anti-pattern i sig selv og ej heller et gammeldags paradigme – det er et pattern, der er baseret på grundlæggende egenskaber ved elektronisk data behandling.
Men det skal også siges, at et hvert pattern kan misbruges og blive til anti-pattern. Min konklusion må være: brug DTO kun der hvor det er nødvendigt.
/Erik