JavaRush /Java Blog /Random-TW /Adapter設計模式解決了什麼問題?

Adapter設計模式解決了什麼問題?

在 Random-TW 群組發布
軟體開發通常因相互協作的組件之間的不相容性而變得複雜。例如,如果您需要將新程式庫與用早期版本的 Java 編寫的舊平台集成,您可能會遇到物件不相容的情況,或者更準確地說,介面不相容的情況。這種情況該怎麼辦?重寫程式碼?但這是不可能的:分析系統將花費大量時間,或工作的內部邏輯將被破壞。Adapter設計模式解決什麼問題-1為了解決這個問題,他們提出了適配器模式,它可以幫助具有不相容介面的物件一起工作。讓我們看看如何使用它!

有關問題的更多詳細信息

首先,讓我們模擬舊系統的行為。假設它會產生上班或上學遲到的原因。為此,我們有一個Excuse包含方法generateExcuse()likeExcuse()和 的介面dislikeExcuse()
public interface Excuse {
   String generateExcuse();
   void likeExcuse(String excuse);
   void dislikeExcuse(String excuse);
}
此介面由類別實作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) {
       // Удаляем элемент из массива
   }
}
讓我們測試一下這個例子:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
結論:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице. 
Прошу меня извинить за непрофессиональное поведение.
現在,我們假設您推出了該服務,收集了統計數據,並注意到大多數服務使用者都是大學生。為了改進它以滿足該組的需求,您專門從另一位開發人員那裡為她訂購了一個藉口生成系統。開發團隊進行了研究、編制評級、連接人工智慧,並添加了與交通擁堵、天氣等的整合。現在你有一個為學生生成藉口的庫,但與其互動的介面不同 - StudentExcuse
public interface StudentExcuse {
   String generateExcuse();
   void dislikeExcuse(String excuse);
}
此介面有兩個方法:generateExcuse,產生藉口,和dislikeExcuse,阻止藉口,使其不再出現。第三方程式庫已關閉編輯 - 您無法變更其原始程式碼。因此,在您的系統中有兩個實作該介面的類Excuse,以及一個包含SuperStudentExcuse實作該介面的類別的函式庫StudentExcuse
public class SuperStudentExcuse implements StudentExcuse {
   @Override
   public String generateExcuse() {
       // Логика нового функционала
       return "Невероятная отговорка, адаптированная под текущее состояние погоды, пробки or сбои в расписании общественного транспорта.";
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Добавляет причину в черный список
   }
}
程式碼無法更改。目前的方案將如下所示: Adapter-2設計模式解決了什麼問題?該版本的系統僅適用於 Excuse 介面。您無法重寫程式碼:在大型應用程式中,此類變更可能需要很長時間或破壞應用程式邏輯。您可以建議引入主介面並增加層次結構: Adapter設計模式解決什麼問題-3為此,您需要重新命名介面Excuse。但在嚴肅的應用程式中,額外的層次結構是不可取的:引入公共根元素會破壞架構。應該實作一個中間類,允許以最小的損失使用新舊功能。簡而言之,您需要一個適配器

適配器模式如何運作

適配器是一種中間對象,它使一個對像上的方法呼叫可以被另一個對象理解。讓我們為我們的範例實作一個適配器並將其命名為Middleware。我們的適配器必須實作與其中一個物件相容的介面。隨它走Excuse。因此,Middleware它可以呼叫第一個物件的方法。 Middleware接收呼叫並將它們以相容的格式傳遞給第二個物件。Middleware這就是methodsgenerateExcuse和的方法的實作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 появятся позже
}
測試(在客戶端程式碼中):
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()); // Адаптер вызывает адаптированный метод
   }
}
結論:
Обычная причина для работника:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице.
Нет оправдания моему поступку. Я недостоин этой должности. Использование нового функционала с помощью адаптера
這是一個令人難以置信的藉口,適合當前的天氣狀況、交通擁堵或公共交通時間表的中斷。該方法generateExcuse只是將呼叫轉移到另一個對象,而不需要額外的轉換。這個方法dislikeExcuse需要先將藉口列入資料庫黑名單。額外的中間資料處理是適配器模式受到喜愛的原因。但是如果方法likeExcuse在介面中Excuse但不在介面中呢StudentExcuse?新功能不支援此操作。對於這種情況,他們提出了一個異常UnsupportedOperationException:如果不支援請求的操作,則會拋出異常。我們就用這個吧。新的類別實作如下所示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.
   }
}
乍一看,這個解決方案似乎並不成功,但模擬功能可能會導致更複雜的情況。如果客戶細心且適配器有詳細記錄,則此解決方案是可以接受的。

何時使用適配器

  1. 如果需要使用第三方類,但其介面與主應用程式不相容。上面的範例顯示如何建立 shim 對象,該對像以目標對象可以理解的格式包裝呼叫。

  2. 當多個現有子類別必須具有共同功能時。最好使用適配器,而不是額外的子類別(它們的建立將導致程式碼重複)。

的優點和缺點

優點:適配器向客戶端隱藏從一個物件到另一個物件的處理請求的細節。客戶端程式碼不會考慮格式化資料或處理對目標方法的呼叫。它太複雜了,而且程式設計師很懶:) 缺點:專案的程式碼庫因額外的類別而變得複雜,如果存在大量不相容點,它們的數量可能會增長到無法控制的大小。

不要與 Facade 和 Decorator 混淆

從表面上看,適配器可能會與外觀和裝飾器模式混淆。Adapter 和 Facade 之間的區別在於 Facade 引入了新的介面並包裝了整個子系統。嗯,與適配器不同,裝飾器更改物件本身,而不是介面。

分步實現演算法

  1. 首先,確保存在該模式可以解決的問題。

  2. 定義一個客戶端接口,將使用另一個類別來代表該接口。

  3. 基於上一個步驟中定義的介面實作適配器類別。

  4. 在適配器類別中,建立一個儲存物件參考的欄位。該引用在建構函數中傳遞。

  5. 在適配器中實作所有客戶端介面方法。此方法可以:

    • 不做任何修改即可轉接通話;

    • 更改資料、增加/減少目標方法的呼叫次數、進一步擴展資料的組成等。

    • 作為最後的手段,如果特定方法不相容,則拋出 UnsupportedOperationException,嚴格需要記錄該異常。

  6. 如果應用程式僅透過用戶端介面使用適配器(如上例所示),則將來可以輕鬆擴展適配器。

當然,設計模式並不是包治百病的靈丹妙藥,但在它的幫助下你可以優雅地解決不同介面的物件不相容的問題。了解基本模式的開發人員比那些只知道如何編寫演算法的開發人員要高出幾個步驟,因為創建嚴肅的應用程式需要它們。重用程式碼變得不那麼困難,維護也變得很愉快。這就是今天的全部內容!但我們很快就會繼續熟悉不同的設計模式:)
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION