JavaRush /Java Blog /Random-IT /Quali problemi risolve il modello di progettazione dell'a...

Quali problemi risolve il modello di progettazione dell'adattatore?

Pubblicato nel gruppo Random-IT
Lo sviluppo del software è spesso complicato dall'incompatibilità tra componenti che funzionano tra loro. Ad esempio, se devi integrare una nuova libreria con una vecchia piattaforma scritta con versioni precedenti di Java, potresti riscontrare incompatibilità di oggetti o, più precisamente, di interfacce. Cosa fare in questo caso? Riscrivere il codice? Ma questo è impossibile: l’analisi del sistema richiederà molto tempo altrimenti la logica interna del lavoro verrà rotta. Quali problemi risolve il modello di progettazione dell'adattatore - 1Per risolvere questo problema, hanno ideato il modello Adapter, che aiuta gli oggetti con interfacce incompatibili a lavorare insieme. Vediamo come usarlo!

Maggiori dettagli sul problema

Per prima cosa simuliamo il comportamento del vecchio sistema. Diciamo che genera ragioni per arrivare in ritardo al lavoro o a scuola. Per fare ciò, abbiamo un'interfaccia Excuseche contiene i metodi generateExcuse(), likeExcuse()e dislikeExcuse().
public interface Excuse {
   String generateExcuse();
   void likeExcuse(String excuse);
   void dislikeExcuse(String excuse);
}
Questa interfaccia è implementata dalla 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) {
       // Удаляем элемент из массива
   }
}
Proviamo l'esempio:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
Conclusione:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице. 
Прошу меня извинить за непрофессиональное поведение.
Ora immaginiamo che tu abbia lanciato il servizio, raccolto statistiche e notato che la maggior parte degli utenti del servizio sono studenti universitari. Per migliorarlo in base alle esigenze di questo gruppo, hai ordinato un sistema di generazione di scuse da un altro sviluppatore appositamente per lei. Il team di sviluppo ha condotto ricerche, compilato valutazioni, collegato l'intelligenza artificiale, aggiunto l'integrazione con ingorghi, condizioni meteorologiche e così via. Ora hai una libreria per generare scuse per gli studenti, ma l'interfaccia per interagire con essa è diversa StudentExcuse:
public interface StudentExcuse {
   String generateExcuse();
   void dislikeExcuse(String excuse);
}
L'interfaccia dispone di due metodi: generateExcuse, che genera una scusa, e dislikeExcuse, che blocca la scusa in modo che non appaia in futuro. Una libreria di terze parti è chiusa per modifiche: non puoi modificare il suo codice sorgente. Di conseguenza, nel tuo sistema ci sono due classi che implementano l'interfaccia Excusee una libreria con una classe SuperStudentExcuseche implementa l'interfaccia StudentExcuse:
public class SuperStudentExcuse implements StudentExcuse {
   @Override
   public String generateExcuse() {
       // Логика нового функционала
       return "Невероятная отговорка, адаптированная под текущее состояние погоды, пробки or сбои в расписании общественного транспорта.";
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Добавляет причину в черный список
   }
}
Il codice non può essere modificato. Lo schema attuale sarà simile al seguente: Quali problemi risolve il modello di progettazione Adapter - 2?Questa versione del sistema funziona solo con l'interfaccia Excuse. Non è possibile riscrivere il codice: in un'applicazione di grandi dimensioni, tali modifiche possono richiedere molto tempo o interrompere la logica dell'applicazione. Puoi suggerire di introdurre l'interfaccia principale e aumentare la gerarchia: Quali problemi risolve il modello di progettazione dell'adattatore - 3per fare ciò, devi rinominare l'interfaccia Excuse. Ma la gerarchia aggiuntiva non è auspicabile nelle applicazioni serie: l'introduzione di un elemento root comune rompe l'architettura. Dovresti implementare una classe intermedia che ti consenta di utilizzare le funzionalità nuove e vecchie con una perdita minima. In breve, è necessario un adattatore .

Come funziona il modello Adattatore

Un adattatore è un oggetto intermedio che rende le chiamate ai metodi su un oggetto comprensibili a un altro. Implementiamo un adattatore per il nostro esempio e chiamiamolo Middleware. Il nostro adattatore deve implementare un'interfaccia compatibile con uno degli oggetti. Lascia fare Excuse. Grazie a ciò Middlewarepuò chiamare metodi del primo oggetto. Middlewarericeve le chiamate e le passa al secondo oggetto in un formato compatibile. Ecco come appare l'implementazione di un metodo Middlewarecon metodi generateExcusee 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 появятся позже
}
Test (nel codice 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()); // Адаптер вызывает адаптированный метод
   }
}
Conclusione:
Обычная причина для работника:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице.
Нет оправдания моему поступку. Я недостоин этой должности. Использование нового функционала с помощью адаптера
Una scusa incredibile, adattata alle condizioni meteorologiche attuali, agli ingorghi o alle interruzioni nell'orario dei trasporti pubblici. Il metodo generateExcusetrasferisce semplicemente la chiamata ad un altro oggetto, senza ulteriori trasformazioni. Il metodo dislikeExcuserichiedeva prima di inserire la scusa in una lista nera del database. L'ulteriore elaborazione intermedia dei dati è il motivo per cui il modello Adapter è amato. Ma che dire di un metodo likeExcusepresente nell'interfaccia Excusema non in StudentExcuse? Questa operazione non è supportata nella nuova funzionalità. In questo caso è stata creata un'eccezione UnsupportedOperationException: viene lanciata se l'operazione richiesta non è supportata. Usiamo questo. Ecco come appare l'implementazione della nuova 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.
   }
}
A prima vista questa soluzione non sembra vincente, ma simularne la funzionalità può portare a una situazione più complessa. Se il cliente è attento e l'adattatore è ben documentato, questa soluzione è accettabile.

Quando utilizzare l'adattatore

  1. Se devi utilizzare una classe di terze parti, ma la sua interfaccia non è compatibile con l'applicazione principale. L'esempio sopra mostra come viene creato un oggetto shim che racchiude le chiamate in un formato comprensibile per l'oggetto di destinazione.

  2. Quando diverse sottoclassi esistenti devono avere funzionalità comuni. Invece di sottoclassi aggiuntive (la loro creazione porterà alla duplicazione del codice), è meglio utilizzare un adattatore.

Vantaggi e svantaggi

Vantaggio: l'adattatore nasconde al client i dettagli dell'elaborazione delle richieste da un oggetto all'altro. Il codice client non pensa alla formattazione dei dati o alla gestione delle chiamate al metodo di destinazione. È troppo complicato e i programmatori sono pigri :) Svantaggio: il codice base del progetto è complicato da classi aggiuntive e se è presente un gran numero di punti incompatibili, il loro numero può raggiungere dimensioni incontrollabili.

Da non confondere con Facciata e Decoratore

Ad un esame superficiale l'Adattatore può essere confuso con i modelli Façade e Decorator. La differenza tra un Adapter e una Facade è che una Facade introduce una nuova interfaccia e avvolge un intero sottosistema. Ebbene, il Decoratore, a differenza dell'Adattatore, modifica l'oggetto stesso, non l'interfaccia.

Algoritmo di implementazione passo passo

  1. Innanzitutto, assicurati che ci sia un problema che questo modello può risolvere.

  2. Definire un'interfaccia client per conto della quale verrà utilizzata un'altra classe.

  3. Implementare una classe dell'adattatore basata sull'interfaccia definita nel passaggio precedente.

  4. Nella classe dell'adattatore, crea un campo che memorizzi un riferimento all'oggetto. Questo riferimento viene passato al costruttore.

  5. Implementare tutti i metodi dell'interfaccia client nell'adattatore. Il metodo può:

    • Trasferire la chiamata senza modifiche;

    • Modificare i dati, aumentare/diminuire il numero di chiamate al metodo di destinazione, espandere ulteriormente la composizione dei dati, ecc.

    • Come ultima risorsa, se un metodo particolare è incompatibile, lancia un'eccezione UnsupportedOperationException, che deve essere rigorosamente documentata.

  6. Se l'applicazione utilizza l'adattatore solo tramite l'interfaccia client (come nell'esempio precedente), ciò consentirà in futuro di estendere gli adattatori senza problemi.

Naturalmente, un design pattern non è una panacea per tutti i mali, ma con il suo aiuto puoi risolvere elegantemente il problema dell'incompatibilità di oggetti con interfacce diverse. Uno sviluppatore che conosce i modelli di base è diversi gradini sopra quelli che sanno semplicemente come scrivere algoritmi, perché sono necessari per creare applicazioni serie. Riutilizzare il codice diventa meno difficile e mantenerlo è un piacere. È tutto per oggi! Ma presto continueremo la nostra conoscenza con diversi modelli di design :)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION