JavaRush /Java Blogu /Random-AZ /Adapter dizayn nümunəsi hansı problemləri həll edir?

Adapter dizayn nümunəsi hansı problemləri həll edir?

Qrupda dərc edilmişdir
Proqram təminatının inkişafı çox vaxt bir-biri ilə işləyən komponentlər arasında uyğunsuzluq səbəbindən çətinləşir. Məsələn, Java-nın əvvəlki versiyalarında yazılmış köhnə platforma ilə yeni kitabxananı inteqrasiya etmək lazımdırsa, siz obyektlərin, daha dəqiq desək, interfeyslərin uyğunsuzluğu ilə qarşılaşa bilərsiniz. Bu halda nə etməli? Kodu yenidən yazın? Amma bu mümkün deyil: sistemin təhlili çox vaxt aparacaq və ya işin daxili məntiqi pozulacaq. Adapter dizayn nümunəsi hansı problemləri həll edir - 1Bu problemi həll etmək üçün uyğun olmayan interfeysləri olan obyektlərin birlikdə işləməsinə kömək edən Adapter nümunəsi ilə gəldilər. Onu necə istifadə edəcəyimizi görək!

Problem haqqında ətraflı məlumat

Əvvəlcə köhnə sistemin davranışını simulyasiya edək. Deyək ki, bu, işə və ya məktəbə gecikmək üçün səbəblər yaradır. Bunun üçün , və Excuseüsullarını ehtiva edən bir interfeysimiz var . generateExcuse()likeExcuse()dislikeExcuse()
public interface Excuse {
   String generateExcuse();
   void likeExcuse(String excuse);
   void dislikeExcuse(String excuse);
}
Bu interfeys sinif tərəfindən həyata keçirilir 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) {
       // Удаляем элемент из массива
   }
}
Nümunəni sınaqdan keçirək:
Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
Nəticə:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице. 
Прошу меня извинить за непрофессиональное поведение.
İndi təsəvvür edək ki, siz xidməti işə saldınız, statistika topladınız və xidmət istifadəçilərinin əksəriyyətinin universitet tələbələri olduğunu gördünüz. Onu bu qrupun ehtiyacları üçün təkmilləşdirmək üçün siz onun üçün başqa bir tərtibatçıdan bəhanə yaratma sistemi sifariş etdiniz. İnkişaf qrupu araşdırma apardı, reytinqlər tərtib etdi, süni intellekti birləşdirdi, tıxaclar, hava şəraiti və s. ilə inteqrasiyanı əlavə etdi. İndi tələbələr üçün bəhanələr yaratmaq üçün kitabxananız var, lakin onunla qarşılıqlı əlaqə üçün interfeys fərqlidir - StudentExcuse:
public interface StudentExcuse {
   String generateExcuse();
   void dislikeExcuse(String excuse);
}
İnterfeysdə iki üsul var: generateExcuse, bəhanə yaradan və dislikeExcuse, bəhanənin gələcəkdə görünməməsi üçün onu bloklayan. Üçüncü tərəf kitabxanası redaktə üçün bağlıdır - onun mənbə kodunu dəyişə bilməzsiniz. Nəticədə, sisteminizdə interfeysi həyata keçirən iki sinif və interfeysi həyata keçirən Excusesinfi olan bir kitabxana var : SuperStudentExcuseStudentExcuse
public class SuperStudentExcuse implements StudentExcuse {
   @Override
   public String generateExcuse() {
       // Логика нового функционала
       return "Невероятная отговорка, адаптированная под текущее состояние погоды, пробки or сбои в расписании общественного транспорта.";
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Добавляет причину в черный список
   }
}
Kod dəyişdirilə bilməz. Cari sxem belə görünəcək: Adapter - 2 dizayn nümunəsi hansı problemləri həll edir?Sistemin bu versiyası yalnız Bəhanə interfeysi ilə işləyir. Kodu yenidən yaza bilməzsiniz: böyük bir tətbiqdə bu cür dəyişikliklər uzun müddət çəkə və ya tətbiq məntiqini poza bilər. Siz əsas interfeysi təqdim etməyi və iyerarxiyanı artırmağı təklif edə bilərsiniz: Adapter dizayn nümunəsi hansı problemləri həll edir - 3Bunu etmək üçün interfeysin adını dəyişmək lazımdır Excuse. Ancaq ciddi tətbiqlərdə əlavə iyerarxiya arzuolunmazdır: ümumi kök elementin təqdim edilməsi arxitekturanı pozur. Yeni və köhnə funksionallığı minimum itki ilə istifadə etməyə imkan verəcək ara sinif tətbiq etməlisiniz. Bir sözlə, bir adapter lazımdır .

Adapter nümunəsi necə işləyir

Adapter bir obyektdə metodlara edilən zəngləri digərinə başa düşülən edən ara obyektdir. Nümunəmiz üçün bir adapter tətbiq edək və onu çağıraq Middleware. Adapterimiz obyektlərdən biri ilə uyğun gələn interfeysi həyata keçirməlidir. Qoy olsun Excuse. Bunun sayəsində Middlewarebirinci obyektin metodlarını çağıra bilir. Middlewarezəngləri qəbul edir və onları uyğun formatda ikinci obyektə ötürür. MiddlewareMetodlarla metodun tətbiqi belədir generateExcusevə belə görünür 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 (müştəri kodunda):
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()); // Адаптер вызывает адаптированный метод
   }
}
Nəticə:
Обычная причина для работника:
Я сегодня опоздал, потому что предпраздничное настроение замедляет метаболические процессы в моем организме и приводит к подавленному состоянию и бессоннице.
Нет оправдания моему поступку. Я недостоин этой должности. Использование нового функционала с помощью адаптера
Mövcud hava şəraitinə, tıxaclara və ya ictimai nəqliyyatın hərəkət cədvəlindəki pozuntulara uyğunlaşdırılmış inanılmaz bəhanə. Metod generateExcuseəlavə çevrilmələr etmədən çağırışı sadəcə başqa obyektə ötürür. Metod dislikeExcuseəvvəlcə bəhanənin verilənlər bazası qara siyahısına salınmasını tələb edirdi. Əlavə aralıq məlumat emalı Adapter modelinin sevilməsinin səbəbidir. likeExcuseBəs interfeysdə olan Excuse, lakin içində olmayan üsul haqqında nə demək olar StudentExcuse? Bu əməliyyat yeni funksionallıqda dəstəklənmir. Bu halda, onlar bir istisna ilə gəldilər UnsupportedOperationException: tələb olunan əməliyyat dəstəklənmirsə atılır. Gəlin bundan istifadə edək. Yeni sinif tətbiqi belə görünür 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.
   }
}
İlk baxışdan bu həll uğurlu görünmür, lakin funksionallığın simulyasiyası daha mürəkkəb vəziyyətə gətirib çıxara bilər. Müştəri diqqətlidirsə və adapter yaxşı sənədləşdirilibsə, bu həll məqbuldur.

Adapterdən nə vaxt istifadə edilməlidir

  1. Üçüncü tərəf sinfindən istifadə etmək lazımdırsa, lakin onun interfeysi əsas proqrama uyğun gəlmir. Yuxarıdakı nümunə zəngləri hədəf obyekt üçün başa düşülən formatda əhatə edən şim obyektinin necə yaradıldığını göstərir.

  2. Bir neçə mövcud alt sinif ümumi funksionallığa malik olmalıdır. Əlavə alt siniflər əvəzinə (onların yaradılması kodun təkrarlanmasına səbəb olacaq) adapterdən istifadə etmək daha yaxşıdır.

Yaxşı və pis tərəfləri

Üstünlük: Adapter bir obyektdən digərinə sorğuların işlənməsinin təfərrüatlarını müştəridən gizlədir. Müştəri kodu məlumatların formatlaşdırılması və ya hədəf metoduna edilən zənglərin idarə edilməsi haqqında düşünmür. Bu, çox mürəkkəbdir, proqramçılar isə tənbəldirlər :) Dezavantaj: Layihənin kod bazası əlavə siniflərlə mürəkkəbdir və çoxlu sayda uyğun olmayan nöqtələr varsa, onların sayı idarə olunmayan ölçülərə qədər arta bilər.

Fasad və Dekorator ilə qarışdırılmamalıdır

Səthi yoxlamadan sonra Adapter Fasad və Dekorator nümunələri ilə qarışdırıla bilər. Adapterlə Fasad arasındakı fərq ondan ibarətdir ki, Fasad yeni interfeys təqdim edir və bütün alt sistemi əhatə edir. Yaxşı, Dekorator, Adapterdən fərqli olaraq, interfeysi deyil, obyektin özünü dəyişir.

Addım-addım həyata keçirmə alqoritmi

  1. Birincisi, bu nümunənin həll edə biləcəyi bir problem olduğundan əmin olun.

  2. Başqa bir sinifin adından istifadə ediləcəyi müştəri interfeysini müəyyənləşdirin.

  3. Əvvəlki addımda müəyyən edilmiş interfeys əsasında adapter sinfini həyata keçirin.

  4. Adapter sinfində obyektə istinad saxlayan sahə yaradın. Bu istinad konstruktorda ötürülür.

  5. Adapterdə bütün müştəri interfeysi üsullarını tətbiq edin. Metod ola bilər:

    • Zəngi dəyişdirmədən köçürmək;

    • Məlumatların dəyişdirilməsi, hədəf metoduna edilən zənglərin sayını artırmaq/azaltmaq, verilənlərin tərkibini daha da genişləndirmək və s.

    • Son çarə olaraq, müəyyən bir üsul uyğun gəlmirsə, ciddi şəkildə sənədləşdirilməli olan UnsupportedOperationException atın.

  6. Tətbiq yalnız müştəri interfeysi vasitəsilə adapterdən istifadə edərsə (yuxarıdakı nümunədə olduğu kimi), bu, adapterlərin gələcəkdə ağrısız şəkildə uzadılmasına imkan verəcəkdir.

Əlbəttə ki, dizayn nümunəsi bütün xəstəliklər üçün panacea deyil, lakin onun köməyi ilə müxtəlif interfeyslərlə obyektlərin uyğunsuzluğu problemini zərif şəkildə həll edə bilərsiniz. Əsas nümunələri bilən bir tərtibatçı, sadəcə alqoritmləri necə yazmağı bilənlərdən bir neçə pillə yuxarıdır, çünki onlar ciddi proqramlar yaratmaq üçün lazımdır. Kodun təkrar istifadəsi daha az çətinləşir və saxlanması xoşdur. Bu gün üçün hamısı budur! Amma tezliklə fərqli dizayn nümunələri ilə tanışlığımızı davam etdirəcəyik :)
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION