JavaRush /Java Blog /Random-KO /Adapter 디자인 패턴은 어떤 문제를 해결합니까?

Adapter 디자인 패턴은 어떤 문제를 해결합니까?

Random-KO 그룹에 게시되었습니다
소프트웨어 개발은 ​​서로 작동하는 구성 요소 간의 비호환성으로 인해 복잡해지는 경우가 많습니다. 예를 들어, 이전 버전의 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클래스가 있는 라이브러리가 있습니다 . SuperStudentExcuseStudentExcuse
public class SuperStudentExcuse implements StudentExcuse {
   @Override
   public String generateExcuse() {
       // Логика нового функционала
       return "Невероятная отговорка, адаптированная под текущее состояние погоды, пробки or сбои в расписании общественного транспорта.";
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Добавляет причину в черный список
   }
}
코드는 변경할 수 없습니다. 현재 구성표는 다음과 같습니다. Adapter - 2 디자인 패턴은 어떤 문제를 해결합니까?이 버전의 시스템은 Excuse 인터페이스에서만 작동합니다. 코드를 다시 작성할 수 없습니다. 대규모 애플리케이션에서는 이러한 변경에 시간이 오래 걸리거나 애플리케이션 논리가 중단될 수 있습니다. 기본 인터페이스를 도입하고 계층 구조를 늘리도록 제안할 수 있습니다. 어댑터 디자인 패턴은 어떤 문제를 해결합니까? - 3이렇게 하려면 인터페이스 이름을 바꿔야 합니다 Excuse. 그러나 심각한 애플리케이션에서는 추가 계층 구조가 바람직하지 않습니다. 공통 루트 요소를 도입하면 아키텍처가 손상됩니다. 최소한의 손실로 새로운 기능과 기존 기능을 사용할 수 있는 중간 클래스를 구현해야 합니다. 즉, 어댑터가 필요합니다 .

어댑터 패턴의 작동 방식

어댑터는 한 개체의 메서드 호출을 다른 개체가 이해할 수 있게 만드는 중간 개체입니다. 우리의 예에 대한 어댑터를 구현하고 이를 이라고 부르겠습니다 Middleware. 어댑터는 개체 중 하나와 호환되는 인터페이스를 구현해야 합니다. 순리에 맡기다 Excuse. 덕분에 Middleware첫 번째 개체의 메서드를 호출할 수 있습니다. Middleware호출을 수신하고 이를 호환 가능한 형식으로 두 번째 개체에 전달합니다. 메소드 와 Middleware메소드를 사용한 메소드 구현은 다음 과 같습니다 . generateExcusedislikeExcuse
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사용하려면 먼저 변명을 데이터베이스 블랙리스트에 올려야 했습니다. 추가적인 중간 데이터 처리가 Adapter 패턴이 사랑받는 이유입니다. 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는 Façade 및 Decorator 패턴과 혼동될 수 있습니다. Adapter와 Facade의 차이점은 Facade가 새로운 인터페이스를 도입하고 전체 하위 시스템을 래핑한다는 것입니다. 글쎄, 데코레이터는 어댑터와 달리 인터페이스가 아닌 객체 자체를 변경합니다.

단계별 구현 알고리즘

  1. 먼저, 이 패턴으로 해결할 수 있는 문제가 있는지 확인하세요.

  2. 다른 클래스를 대신하여 사용할 클라이언트 인터페이스를 정의합니다.

  3. 이전 단계에서 정의한 인터페이스를 기반으로 어댑터 클래스를 구현합니다.

  4. 어댑터 클래스에서 개체에 대한 참조를 저장하는 필드를 만듭니다. 이 참조는 생성자에 전달됩니다.

  5. 어댑터에서 모든 클라이언트 인터페이스 메소드를 구현하십시오. 이 방법은 다음을 수행할 수 있습니다.

    • 수정 없이 통화를 전환합니다.

    • 데이터 변경, 대상 메소드 호출 횟수 증가/감소, 데이터 구성 추가 확장 등

    • 최후의 수단으로, 특정 메소드가 호환되지 않는 경우 UnsupportedOperationException을 발생시키십시오. 이를 엄격하게 문서화해야 합니다.

  6. 응용 프로그램이 클라이언트 인터페이스를 통해서만 어댑터를 사용하는 경우(위의 예와 같이) 향후 어댑터를 문제 없이 확장할 수 있습니다.

물론 디자인 패턴이 모든 문제에 대한 만병통치약은 아니지만 이를 사용하면 다양한 인터페이스를 가진 개체의 비호환성 문제를 우아하게 해결할 수 있습니다. 기본 패턴을 아는 개발자는 단순히 알고리즘 작성 방법을 아는 개발자보다 몇 단계 앞서 있습니다. 왜냐하면 이러한 개발자는 심각한 애플리케이션을 만드는 데 필요하기 때문입니다. 코드 재사용이 덜 어려워지고 유지 관리가 즐거워집니다. 오늘은 그게 다야! 하지만 우리는 곧 다양한 디자인 패턴을 계속 접하게 될 것입니다 :)
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION