Demorou um pouco para descobrir essa resposta e o que ela realmente significa. Alguns exemplos devem torná-lo mais claro.
Proxy
primeiro:
public interface Authorization {
String getToken();
}
E:
// goes to the DB and gets a token for example
public class DBAuthorization implements Authorization {
@Override
public String getToken() {
return "DB-Token";
}
}
E há um interlocutor disso Authorization
, bastante idiota:
class Caller {
void authenticatedUserAction(Authorization authorization) {
System.out.println("doing some action with : " + authorization.getToken());
}
}
Nada incomum até agora, certo? Obtenha um token de um determinado serviço, use esse token. Agora vem mais um requisito para a imagem, adicione log: significando registrar o token sempre. É simples para este caso, basta criar um Proxy
:
public class LoggingDBAuthorization implements Authorization {
private final DBAuthorization dbAuthorization = new DBAuthorization();
@Override
public String getToken() {
String token = dbAuthorization.getToken();
System.out.println("Got token : " + token);
return token;
}
}
Como usaríamos isso?
public static void main(String[] args) {
LoggingDBAuthorization loggingDBAuthorization = new LoggingDBAuthorization();
Caller caller = new Caller();
caller.authenticatedUserAction(loggingDBAuthorization);
}
Observe que LoggingDBAuthorization
contém uma instância de DBAuthorization
. Ambos LoggingDBAuthorization
e DBAuthorization
implementar Authorization
.
- Um proxy realizará alguma implementação concreta (
DBAuthorization
) da interface base ( Authorization
). Em outras palavras, um Proxy sabe exatamente o que está sendo procurado.
Decorator
:
Começa praticamente da mesma forma que Proxy
, com uma interface:
public interface JobSeeker {
int interviewScore();
}
e uma implementação do mesmo:
class Newbie implements JobSeeker {
@Override
public int interviewScore() {
return 10;
}
}
E agora queremos adicionar um candidato mais experiente, que adicione a pontuação da entrevista e a pontuação do outro JobSeeker
:
@RequiredArgsConstructor
public class TwoYearsInTheIndustry implements JobSeeker {
private final JobSeeker jobSeeker;
@Override
public int interviewScore() {
return jobSeeker.interviewScore() + 20;
}
}
Observe como eu disse isso, mais o de outro JobSeeker , não Newbie
. A Decorator
não sabe exatamente o que está decorando, conhece apenas o contrato dessa instância decorada (sabe sobre JobSeeker
). Observe aqui que isso é diferente de a Proxy
; que, ao contrário, sabe exatamente o que está decorando.
Você pode questionar se há realmente alguma diferença entre os dois padrões de design nesse caso? E se tentássemos escrever o Decorator
como a Proxy
?
public class TwoYearsInTheIndustry implements JobSeeker {
private final Newbie newbie = new Newbie();
@Override
public int interviewScore() {
return newbie.interviewScore() + 20;
}
}
Essa é definitivamente uma opção e destaca o quão próximos esses padrões são; eles ainda são destinados a diferentes cenários, conforme explicado nas outras respostas.