2016-03-22 11 views
7

Sono in una situazione molto particolare con una delle classi che sto codificando. Ho questa classe chiamata User che assomiglia a questo:Motivi di progettazione - Come applicare gli attributi degli oggetti solo in alcune situazioni (modello Generatore, Iniezione delle dipendenze)

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 
} 

All'interno mio codice ci sono diverse situazioni in cui ho solo bisogno di un oggetto vuoto di tipo User e quindi solo costruire utilizzando il costruttore di default: new User().

Tuttavia, ho un'altra classe denominata CreateUserRequest che modella una richiesta REST per creare l'utente in un server. Il payload minimo deve contenere gli attributi name, password, email e authenticationRealm inviati in formato JSON.

In questo momento sto manipolazione di questo controllando per questi parametri nel costruttore della richiesta:

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."); 
} 

Questo funziona bene, ma qualcosa è prurito ... vorrei far rispettare questo in un modo più sicuro , in modo che il codice applicasse gli attributi da compilare senza possibilità di generare un'eccezione.

Mi sento come se ci fosse un modo migliore per farlo con un modello di progettazione. Ho pensato di creare una classe UserRequestBuilder, ma ciò implicherebbe anche la possibilità di lanciare un'eccezione nel metodo build() (altrimenti, c'è un modo per garantire che gli attributi vengano compilati prima dello build()?). Iniezione di dipendenza sembra anche una possibilità, ma non sono sicuro di come lo metterei in questo particolare esempio ...

Qualche idea?

+3

Sembra un oggetto diverso, un oggetto CreateUser che magari ha un costruttore e convalida i valori forniti. Qualsiasi motivo devono essere basati sulla stessa classe poiché eseguono due compiti diversi? – dbugger

+1

I miei pensieri immediati sarebbero aggiungere un nuovo metodo isValidUserRequest() sulla classe User. Pensando che la classe User debba decidere da sola se è valida o meno per l'uso in richiesta. –

+0

Grazie per il commento @dbugger. Sì - diverse chiamate REST producono o consumano oggetti JSON con gli stessi attributi della classe 'User' (ad esempio, la classe User rappresenta un'entità remota). Le classi non svolgono realmente compiti diversi. La classe 'CreateUserRequest' crea una chiamata REST che passa un oggetto' Utente' nel suo payload in formato JSON. – Phil

risposta

7

Che ne dici di far funzionare i servizi REST su un utente DTO? (Ovviamente, UserDTO potrebbe essere sostituito con una sottoclasse di User).

È possibile annotare i campi, i setter oi parametri del costruttore su UserDTO con @NonNull e avere gli avvertimenti del compilatore di emissione Checker Framework quando si passano valori nulli invece di nome password, e-mail ecc. A UserDTO.

Utilizzando un framework come Mapstruct, mappatura tra i DTOs servizi REST e gli oggetti di backend è molto facile:

@Mapper 
public interface UserMapper { 

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

    UserDTO map(User user); 

    User map(UserDTO userDTO); 
} 

Sopra codice sarà su di compilazione genera un'implementazione UserMapper, con il codice generato automaticamente per i metodi specificati (- e il codice generato automaticamente si accoppia semplicemente con getter e setter simili, potreste farlo da soli, ma con molte DTO/Entità diventa sempre più noioso e noioso.

Nei DTO è possibile escludere tutti i campi che non si desidera esporre.

Ps. Il mio uso di cui sopra è questo: sto creando un server REST basato su Jersey, cioè l'implementazione di riferimento di JAX-RS. Questo progetto, chiamiamolo A, conosce solo i DTO. I metodi REST chiamano in un altro progetto B, che recupera gli oggetti dal database e li associa al DTO corrispondente, che viene quindi restituito al progetto A. Parte del motivo per questo modello è che le entità del progetto B per ragioni storiche sono ingombra di metodi/funzionalità, che non dovrebbero essere esposti al progetto A.Per quanto riguarda i controlli di integrità (da JSON a DTO), jersey supporta Bean Validation, vale a dire che il framework convaliderà i bean di input di ciascuna risorsa di riposo se annotati con @Valid. È anche possibile creare le proprie annotazioni personalizzate, che hanno definito un VincoloValidatore. Il framework di validazione del bean controllerà questi vincoli sui parametri del metodo REST della jersey annotati. Vedere https://jersey.java.net/documentation/latest/bean-validation.html#d0e13690

+0

Approccio molto interessante, @Hervian. Lo proverò e lo contrassegnerò come risposta se funziona. Grazie. – Phil

+0

Ho seguito un approccio molto simile e ha funzionato. Grazie. – Phil

Problemi correlati