Často je to např. Long, pokud je datovým podkladem SQL databáze ale setkávám se i se Stringem, když je entita ze vzdáleného systému volaného přes MQ, WebServices, Rest apod.
Problém pak nastává v tom, že se tyto identifikátory často pletou.
Například
public void updateTransaction(Long transactionId, Long userId, Long operatorId, Long clientId) { // ... do some nasty business logic }
Není jasné, jestli userId je identifikátor entity User - nejspíš ano. A co operatorId? Je to taky z entity User? Obzvláště, když žádná entita Operator v doménovém modelu není.
Co kdyby rozhraní vypadalo takto
public void updateTransaction(TransactionId transactionId, UserId userId, UserId operatorId, ClientId clientId) { // ... do some nasty business logic }
Takto už je sématický význam parametrů jasnější. Co ale ve skutečnosti obsahuje třída UserId?
public final class UserId { private final Long value; private UserId(Long value) { this.value = value; } public Long getValue() { return value; } public static UserId of(Long value) { if (value==null) throw new IllegalArgumentException("Parameter 'value' can't be null."); return new UserId(value); } public static UserId of(String value) { if (value==null) throw new IllegalArgumentException("Parameter 'value' can't be null."); return new UserId(Long.valueOf(value)); } }
Instance je immutable a je tedy thread-safe. Zároveň hlídá, že hodnota value je ne-null-ová. Může obsahovat tolik factory metod kolik je potřeba - v tomto případě umí instanciovat z Long a ze String.
Co když používám Hibernate? Jak to mám namapovat? Můžeme nentitu nechat tak jak je - anotovanou nad attributy - pak Hibernate mapuje hodnoty přímo na atributy bez použití set a get metod.
@Entity @Table(name = "COOL_USER") public class User { @Id @GeneratedValue @Column(name = "USER_ID") private Long id; @Column(name = "FIRST_NAME") private String firstName; @Column(name = "LAST_NAME") private String lastName; public UserId getId() { return UserId.of(id); } public void setId(UserId id) { this.id = id.getId(); } // other set get methods for firstName and lastName }
Čitelnost servisního kódy je mnohem lepší a navíd kompilátor za vás dělá kontrolu, jestli jste parametry nepopletli.
Myslím si, že podobný přístup by se dal použít i na jiné jednoduché datové typy - např. AccountNumber, BirthNumber, DateOfBirth místo String, String, Date.
Žádné komentáře:
Okomentovat