2016-03-22 28 views
7

Jestem w bardzo szczególnej sytuacji z jedną z klas, które koduję. Mam tej klasy o nazwie User, który wygląda tak:Wzorce projektowe - jak wymusić atrybuty obiektów tylko w niektórych sytuacjach (wzorzec Konstruktora, iniekcja zależności)

public class User { 
    private long id; // + getters and setters 
    private boolean isDeletable; // + getters and setters 
    private String name; // + getters and setters 
    private String password; // + getters and setters 
    private String email; // + getters and setters 
    private String authenticationRealm; // + getters and setters 
    private String displayName; // + getters and setters 
    private Date deletedDate; // + getters and setters 
} 

ciągu mojego kodu istnieje kilka sytuacji, w których po prostu trzeba pusty obiekt typu User i dlatego właśnie go zbudować przy użyciu domyślnego konstruktora: new User().

Jednak mam inną klasę o nazwie CreateUserRequest, która modeluje żądanie REST, aby utworzyć użytkownika na serwerze. Minimalne obciążenie musi zawierać atrybuty wysłane w formacie JSON o następujących atrybutach: name, password, email i .

Teraz jestem obsługi to poprzez sprawdzenie tych parametrów konstruktora życzenie:

public CreateUserRequest(User user) { 
    if(user.getName() == null || user.getPassword() == null || user.getEmail() == null || user.getAuthenticationRealm() == null) 
     throw new RuntimeException("Not enough attributes in User object. Minimum: name, password, e-mail and authentication realm."); 
} 

To działa OK, ale coś jest swędzenie ... Chciałbym, aby egzekwować to w bezpieczniejszy sposób , aby kod wymusił zapełnianie atrybutów bez możliwości zgłaszania wyjątku.

Czuję, że musi istnieć lepszy sposób na zrobienie tego za pomocą wzoru. Pomyślałem o utworzeniu klasy UserRequestBuilder, ale to również prawdopodobnie oznaczałoby odrzucenie wyjątku w metodzie build() (w przeciwnym razie czy jest sposób, w jaki mogę zagwarantować, że atrybuty zostaną wypełnione przed build()?). Wstrzyknięcie zależne od zależności również brzmi jak możliwość, ale nie jestem pewien, w jaki sposób umieściłbym go w tym konkretnym przykładzie ...

Jakieś myśli?

+3

Brzmi jak inny obiekt, obiekt CreateUser prawdopodobnie ma konstruktor i sprawdza poprawność dostarczonych wartości. Jakieś powody, dla których muszą być oparte na tej samej klasie, ponieważ wykonują dwa różne zadania? – dbugger

+1

Moje natychmiastowe myśli byłyby dodanie nowej metody isValidUserRequest() w klasie użytkownika. Myśląc, że klasa Użytkownika powinna sama zdecydować, czy jest ona ważna, czy nie, aby użyć jej na żądanie. –

+0

Dzięki za komentarz @dbugger. Tak - kilka wywołań REST generuje lub zużywa obiekty JSON z tymi samymi atrybutami klasy "Użytkownik" (tj. Klasa użytkownika reprezentuje zdalny obiekt). Klasy tak naprawdę nie wykonują różnych zadań. Klasa "CreateUserRequest' buduje wywołanie REST, które przekazuje obiekt' User' w swoim ładunku w formacie JSON. – Phil

Odpowiedz

7

Co powiesz na to, że Twoje usługi REST działają na Użytkowniku DTO? (Oczywiście, UserDTO można zastąpić podklasą Użytkownika).

Można dodawać adnotacje do pól, ustawiaczy lub parametrów konstruktora na UserDTO za pomocą @NonNull i mieć ostrzeżenia kompilatora wydania Checker Framework podczas przekazywania wartości pustych zamiast nazwy hasła, adresu e-mail itp. Do UserDTO.

Korzystanie ramy jak Mapstruct, mapowanie pomiędzy DTOs usług REST oraz obiektów zaplecza jest bardzo proste:

@Mapper 
public interface UserMapper { 

    public static final UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); 

    UserDTO map(User user); 

    User map(UserDTO userDTO); 
} 

Powyższy kod po kompilacji wygenerowania realizację UserMapper, kod wygenerowany automatycznie dla określonych metod (- a wygenerowany automatycznie kod po prostu paruje podobnie nazwane programy pobierające i ustawiające, możesz to zrobić sam, ale w przypadku wielu DTO/jednostek staje się czasochłonny i nudny).

W DTO możesz wykluczyć wszystkie te pola, których nie chcesz ujawniać.

Ps. Moje własne użycie powyższego jest następujące: Tworzę serwer REST oparty na Jersey, tj. Referencyjną implementację JAX-RS. Ten projekt, nazywany A, wie tylko o DTO. Metody REST wywołują inny projekt B, który pobiera obiekty z bazy danych i odwzorowuje je na odpowiedni DTO, który jest następnie zwracany do projektu A. Część przyczyn tego wzorca polega na tym, że podmioty projektu B ze względów historycznych są zagracone metodami/funkcjonalnościami, które nie powinny być narażone na projekt A.Jeśli chodzi o kontrolę poprawności (JSON do DTO), jersey obsługuje Bean Validation, co oznacza, że ​​framework sprawdzi wszystkie komponenty wejściowe zasobów rest, jeśli są oznaczone adnotacją @Valid. Możliwe jest również tworzenie własnych adnotacji z definicją ConstraintValidator. Struktura sprawdzania fasoli sprawdzi te ograniczenia w parametrach metody REST z przypisanymi koszulkami. Zobacz https://jersey.java.net/documentation/latest/bean-validation.html#d0e13690

+0

Bardzo interesujące podejście, @Hervian. Wypróbuję to i oznaczę jako odpowiedź, jeśli zadziała. Dziękuję Ci. – Phil

+0

Zastosowałem bardzo podobne podejście i zadziałało. Dzięki. – Phil