Mais detalhes sobre o problema
Primeiro, vamos simular o comportamento do sistema antigo. Digamos que gere motivos para chegar atrasado ao trabalho ou à escola. Para fazer isso, temos uma interface que contémExcuse
os métodos generateExcuse()
e likeExcuse()
.dislikeExcuse()
public interface Excuse {
String generateExcuse();
void likeExcuse(String excuse);
void dislikeExcuse(String excuse);
}
Esta interface é implementada pela classe WorkExcuse
:
public class WorkExcuse implements Excuse {
private String[] reasonOptions = {"по невероятному стечению обстоятельств у нас в доме закончилась горячая вода и я ждал, пока солнечный свет, сконцентрированный через лупу, нагреет кружку воды, чтобы я мог умыться.",
"искусственный интеллект в моем будильнике подвел меня и разбудил на час раньше обычного. Поскольку сейчас зима, я думал что еще ночь и уснул. Дальше все How в тумане.",
"предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице."};
private String[] sorryOptions = {"Это, конечно, не повторится, мне очень жаль.", "Прошу меня извинить за непрофессиональное поведение.", "Нет оправдания моему поступку. Я недостоин этой должности."};
@Override
public String generateExcuse() { // Случайно выбираем отговорку из массива
String result = "Я сегодня опоздал, потому что " + reasonOptions[(int) Math.round(Math.random() + 1)] + "\n" +
sorryOptions[(int) Math.round(Math.random() + 1)];
return result;
}
@Override
public void likeExcuse(String excuse) {
// Дублируем элемент в массиве, чтобы шанс его выпадения был выше
}
@Override
public void dislikeExcuse(String excuse) {
// Удаляем элемент из массива
}
}
Vamos testar o exemplo:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
Conclusão:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице.
Прошу меня извинить за непрофессиональное поведение.
Agora vamos imaginar que você lançou o serviço, coletou estatísticas e percebeu que a maioria dos usuários do serviço são estudantes universitários. Para melhorá-lo para as necessidades deste grupo, você encomendou um sistema de geração de desculpas de outro desenvolvedor especificamente para ela. A equipe de desenvolvimento conduziu pesquisas, compilou classificações, conectou inteligência artificial, adicionou integração com engarrafamentos, clima e assim por diante. Agora você tem uma biblioteca para gerar desculpas para os alunos, mas a interface para interagir com ela é diferente - StudentExcuse
:
public interface StudentExcuse {
String generateExcuse();
void dislikeExcuse(String excuse);
}
A interface possui dois métodos: generateExcuse
, que gera uma desculpa, e dislikeExcuse
, que bloqueia a desculpa para que ela não apareça no futuro. Uma biblioteca de terceiros está fechada para edição - você não pode alterar seu código-fonte. Como resultado, em seu sistema existem duas classes que implementam a interface Excuse
e uma biblioteca com uma classe SuperStudentExcuse
que implementa a interface StudentExcuse
:
public class SuperStudentExcuse implements StudentExcuse {
@Override
public String generateExcuse() {
// Логика нового функционала
return "Невероятная отговорка, адаптированная под текущее состояние погоды, пробки or сбои в расписании общественного транспорта.";
}
@Override
public void dislikeExcuse(String excuse) {
// Добавляет причину в черный список
}
}
O código não pode ser alterado. O esquema atual ficará assim: Esta versão do sistema só funciona com a interface Excuse. Você não pode reescrever o código: em um aplicativo grande, essas alterações podem demorar muito ou interromper a lógica do aplicativo. Você pode sugerir a introdução da interface principal e o aumento da hierarquia: Para fazer isso, você precisa renomear a interface Excuse
. Mas a hierarquia adicional é indesejável em aplicações sérias: a introdução de um elemento raiz comum quebra a arquitetura. Deve ser implementada uma classe intermediária que permitirá o uso de funcionalidades novas e antigas com perdas mínimas. Resumindo, você precisa de um adaptador .
Como funciona o padrão Adaptador
Um adaptador é um objeto intermediário que torna as chamadas aos métodos de um objeto compreensíveis para outro. Vamos implementar um adaptador para nosso exemplo e chamá-lo deMiddleware
. Nosso adaptador deve implementar uma interface compatível com um dos objetos. Deixe estar Excuse
. Graças a isso, Middleware
ele pode chamar métodos do primeiro objeto. Middleware
recebe chamadas e as passa para o segundo objeto em um formato compatível. Esta é a aparência da implementação de um método Middleware
com métodos generateExcuse
e dislikeExcuse
:
public class Middleware implements Excuse { // 1. Middleware становится совместимым с an objectом WorkExcuse через интерфейс Excuse
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) { // 2. Получаем ссылку на адаптируемый an object
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse(); // 3. Адаптер реализовывает метод интерфейса
}
@Override
public void dislikeExcuse(String excuse) {
// Метод предварительно помещает отговорку в черный список БД,
// Затем передает ее в метод dislikeExcuse an object superStudentExcuse.
}
// Метод likeExcuse появятся позже
}
Teste (no código do cliente):
public class Test {
public static void main(String[] args) {
Excuse excuse = new WorkExcuse(); // Создаются an objectы классов,
StudentExcuse newExcuse = new SuperStudentExcuse(); // Которые должны быть совмещены.
System.out.println("Обычная причина для работника:");
System.out.println(excuse.generateExcuse());
System.out.println("\n");
Excuse adaptedStudentExcuse = new Middleware(newExcuse); // Оборачиваем новый функционал в an object-адаптер
System.out.println("Использование нового функционала с помощью адаптера:");
System.out.println(adaptedStudentExcuse.generateExcuse()); // Адаптер вызывает адаптированный метод
}
}
Conclusão:
Обычная причина для работника:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице.
Нет оправдания моему поступку. Я недостоин этой должности.
Использование нового функционала с помощью адаптера
Uma desculpa incrível, adaptada ao estado atual do tempo, engarrafamentos ou perturbações nos horários dos transportes públicos. O método generateExcuse
simplesmente transfere a chamada para outro objeto, sem transformações adicionais. O método dislikeExcuse
exigia primeiro colocar a desculpa em uma lista negra de banco de dados. O processamento adicional de dados intermediários é a razão pela qual o padrão Adapter é adorado. Mas e quanto a um método likeExcuse
que está na interface Excuse
, mas não na StudentExcuse
? Esta operação não é suportada na nova funcionalidade. Para este caso, eles criaram uma exceção UnsupportedOperationException
: ela será lançada se a operação solicitada não for suportada. Vamos usar isso. Esta é a aparência da nova implementação da classe Middleware
:
public class Middleware implements Excuse {
private StudentExcuse superStudentExcuse;
public Middleware(StudentExcuse excuse) {
this.superStudentExcuse = excuse;
}
@Override
public String generateExcuse() {
return superStudentExcuse.generateExcuse();
}
@Override
public void likeExcuse(String excuse) {
throw new UnsupportedOperationException("Метод likeExcuse не поддерживается в новом функционале");
}
@Override
public void dislikeExcuse(String excuse) {
// Метод обращается за дополнительной информацией к БД,
// Затем передает ее в метод dislikeExcuse an object superStudentExcuse.
}
}
À primeira vista, esta solução não parece bem-sucedida, mas simular a funcionalidade pode levar a uma situação mais complexa. Se o cliente estiver atento e o adaptador estiver bem documentado, esta solução é aceitável.
Quando usar o adaptador
-
Se precisar usar uma classe de terceiros, mas sua interface não é compatível com o aplicativo principal. O exemplo acima mostra como é criado um objeto shim que agrupa as chamadas em um formato compreensível para o objeto de destino.
-
Quando várias subclasses existentes devem ter funcionalidades comuns. Em vez de subclasses adicionais (sua criação levará à duplicação de código), é melhor usar um adaptador.
Vantagens e desvantagens
Vantagem: O adaptador oculta do cliente os detalhes do processamento de solicitações de um objeto para outro. O código do cliente não pensa em formatar os dados ou manipular chamadas para o método de destino. É muito complicado e os programadores são preguiçosos :) Desvantagem: a base de código do projeto é complicada por classes adicionais e, se houver um grande número de pontos incompatíveis, seu número pode crescer para tamanhos incontroláveis.Não confundir com Fachada e Decorador
Após um exame superficial, o Adaptador pode ser confundido com os padrões Fachada e Decorador. A diferença entre um Adaptador e uma Fachada é que uma Fachada introduz uma nova interface e envolve um subsistema inteiro. Bem, o Decorator, ao contrário do Adapter, altera o objeto em si, não a interface.Algoritmo de implementação passo a passo
-
Primeiro, certifique-se de que existe um problema que esse padrão possa resolver.
-
Defina uma interface cliente em nome da qual outra classe será usada.
-
Implemente uma classe de adaptador baseada na interface definida na etapa anterior.
-
Na classe do adaptador, crie um campo que armazene uma referência ao objeto. Esta referência é passada no construtor.
-
Implemente todos os métodos de interface do cliente no adaptador. O método pode:
-
Transfira a chamada sem modificação;
-
Altere dados, aumente/diminua o número de chamadas para o método de destino, expanda ainda mais a composição dos dados, etc.
-
Como último recurso, se um método específico for incompatível, lance uma UnsupportedOperationException, que precisa ser estritamente documentada.
-
-
Se o aplicativo usar o adaptador apenas por meio da interface do cliente (como no exemplo acima), isso permitirá que os adaptadores sejam estendidos sem problemas no futuro.
GO TO FULL VERSION