Ecco cosa viene fornito:
public interface Request {}
// there are 20 subclasses of Request
public class CreateUserRequest implements Request {
@NotEmpty
public String userName;
}
// request processor is a thing that aimed to process requests
public interface RequestProcessor<TRequest extends Request> {
boolean processRequest(TRequest request);
}
public class ServiceFacade {
// 20 processors like this one
private final RequestProcessor<CreateUserRequest> createUserRequestProcessor;
public ServiceFacade(
RequestProcessor<CreateUserRequest> createUserRequestProcessor) {
this.createUserRequestProcessor = createUserRequestProcessor;
}
// 20 methods like this one
public boolean createUser(CreateUserRequest request) {
createUserRequestProcessor.processRequest(request);
}
}
Un oggetto che implementa RequestProcessor<T>
ha lo scopo di elaborare richieste di tipo T
. Inoltre, è responsabile della gestione di tutti gli errori correlati (come richiesta non valida, errore di accesso al database e così via). Pertanto, il flusso di lavoro per CreateUserRequest
dovrebbe essere simile a questo:
request
validate/throw
make sure user doesn't exist yet/throw
create user
Come al solito, altri tipi di richieste potrebbero richiedere ulteriori passaggi da eseguire.
Come illustrazione : in caso di AddUserAsFriendRequest
dovremo verificare che entrambi gli utenti esistano e che il candidato amico abbia permesso ad altri di aggiungerlo come amico. Oh, e anche, se un utente aggiunge più di 10 amici in un giorno, è limitato a 1 amico per 2 ore per il resto della giornata.
La domanda è , quale gerarchia / struttura / approccio dovrei usare per poter estendere questa funzionalità sia in "numero di tipi di richieste" che in "flusso di lavoro del processo di richiesta".
Ci sono solo 2 approcci che vedo.
Inheritance
public abstract class ValidatingRequestProcessor<TRequest extends Request>
implements RequestProcessor<TRequest> {
protected final Validator validator;
public ValidatingRequestProcessor(Validator validator) {
this.validator = validator;
}
public boolean processRequest(TRequest request) {
Set<ConstraintViolation<TRequest>> violations =
validator.validate(request);
if(!violations.isEmpty()) {
return false;
}
return processValidatedRequest(request);
}
protected abstract boolean processValidatedRequest(TRequest request);
}
public class CreateUserValidatingRequestProcessor
extends ValidatingRequestProcessor<CreateUserRequest> {
public CreateUserValidatingRequestProcessor(Validator validator) {
super(validator);
}
protected boolean processValidatedRequest(TRequest request) {
// request is valid, so I can try to create user here
}
}
e così via qui. In caso di N
"steps logici", avrò (N-1)
nidificato extends
e N
classi:
class A implements RequestProcessor {}
class B extends A {}
class C extends B {}
class D extends C {}
...
class Z extends Y {}
RequestProcessor requestProcessor = new Z(); // TADA!
Composizione
public class ChainingRequestProcessor<TRequest extends Request>
implements RequestProcessor<TRequest> {
private final RequestProcessor<TRequest> processorA;
private final RequestProcessor<TRequest> processorB;
public ChainingRequestProcessor(
RequestProcessor<TRequest> processorA,
RequestProcessor<TRequest> processorB) {
this.processorA = processorA;
this.processorB = processorB;
}
public boolean processRequest(TRequest request) {
return processorA.processRequest(request) &&
processorB.processRequest(request);
}
}
public RequestValidatorProcessor<TRequest extends Request>
implements RequestProcessor<TRequest> {
private final Validator validator;
public RequestValidatorProcessor(Validator validator) {
this.validator = validator;
}
public boolean processRequest(TRequest request) {
return !validator.validate(request).isEmpty();
}
}
public CreateUserProcessor
implements RequestProcessor<CreateUserRequest> {
public boolean processRequest(CreateUserRequest request) {
// not sure whether request is valid or not
// can try to create user here
}
}
e così via. In caso di N
"steps logici" avrò solo N
classes. Quindi:
class A implements RequestProcessor {}
class B implements RequestProcessor {}
class C implements RequestProcessor {}
class D implements RequestProcessor {}
...
class Z implements RequestProcessor {}
// just guess
RequestProcessor requestProcessor = new A(new B(), new C(new D())); // TADA!
Quale approccio è migliore per questo compito? Meglio significa:
- Più facile modificare la logica
- Più facile aggiungere logica
- Più facile da testare