JavaRush /Java блогы /Random-KK /Адаптерді жобалау үлгісі қандай мәселелерді шешеді?

Адаптерді жобалау үлгісі қандай мәселелерді шешеді?

Топта жарияланған
Бағдарламалық қамтамасыз етуді әзірлеу көбінесе бір-бірімен жұмыс істейтін құрамдастардың сәйкес келмеуімен қиындайды. Мысалы, Java-ның бұрынғы нұсқаларында жазылған ескі платформамен жаңа кітапхананы біріктіру қажет болса, нысандардың, дәлірек айтқанда интерфейстердің үйлесімсіздігіне тап болуыңыз мүмкін. Бұл жағдайда не істеу керек? Кодты қайта жазу керек пе? Бірақ бұл мүмкін емес: жүйені талдау көп уақытты алады немесе жұмыстың ішкі логикасы бұзылады. Адаптерді жобалау үлгісі қандай мәселелерді шешеді - 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, ол болашақта пайда болмас үшін ақтауды блоктайды. Үшінші тарап кітапханасы өңдеуге жабылған - оның бастапқы codeын өзгерте алмайсыз. Нәтижесінде, сіздің жүйеңізде интерфейсті жүзеге асыратын екі сынып және интерфейсті жүзеге асыратын Excuseкласы бар кітапхана бар : SuperStudentExcuseStudentExcuse
public class SuperStudentExcuse implements StudentExcuse {
   @Override
   public String generateExcuse() {
       // Логика нового функционала
       return "Невероятная отговорка, адаптированная под текущее состояние погоды, пробки or сбои в расписании общественного транспорта.";
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Добавляет причину в черный список
   }
}
Кодты өзгерту мүмкін емес. Ағымдағы схема келесідей болады: Адаптер - 2 дизайн үлгісі қандай мәселелерді шешеді?Жүйенің бұл нұсқасы тек Excuse интерфейсімен жұмыс істейді. Кодты қайта жаза алмайсыз: үлкен қолданбада мұндай өзгертулер ұзақ уақыт алуы немесе қолданба логикасын бұзуы мүмкін. Сіз негізгі интерфейсті енгізуді және иерархияны ұлғайтуды ұсына аласыз: Адаптерді жобалау үлгісі қандай мәселелерді шешеді - 3Бұл әрекетті орындау үшін интерфейстің атын өзгерту керек Excuse. Бірақ қосымша иерархия маңызды қолданбаларда қажет емес: жалпы түбір элементін енгізу архитектураны бұзады. Жаңа және ескі функционалдылықты аз шығынмен пайдалануға мүмкіндік беретін аралық сыныпты енгізу керек. Қысқасы, сізге адаптер қажет .

Адаптер үлгісі қалай жұмыс істейді

Адаптер – бір an objectідегі әдістерге шақыруларды екіншісіне түсінікті ететін аралық an object. Біздің мысалға адаптерді іске асырып, оны шақырайық Middleware. Біздің адаптер нысандардың бірімен үйлесімді интерфейсті жүзеге асыруы керек. Болсын Excuse. Осының арқасында Middlewareол бірінші an objectінің әдістерін шақыра алады. Middlewareқоңырауларды қабылдайды және оларды үйлесімді форматта екінші нысанға береді. MiddlewareӘдіспен әдісті іске асыру мынау generateExcuseжәне келесідей көрінеді 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 появятся позже
}
Тестілеу (клиент codeында):
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. Егер сізге үшінші тарап класын пайдалану қажет болса, бірақ оның интерфейсі негізгі қолданбамен үйлесімді болмаса. Жоғарыдағы мысал қоңырауларды мақсатты нысанға түсінікті пішімге айналдыратын шығыр нысанының қалай жасалғанын көрсетеді.

  2. Бірнеше бар ішкі сыныптардың ортақ функционалдығы болуы керек кезде. Қосымша ішкі сыныптардың орнына (олардың жасалуы codeтың қайталануына әкеледі), адаптерді қолданған дұрыс.

Артылықшылықтар мен кемшіліктер

Артықшылығы: Адаптер клиенттен бір нысаннан екіншісіне сұраныстарды өңдеу мәліметтерін жасырады. Клиент codeы деректерді пішімдеу немесе мақсатты әдіске қоңырауларды өңдеу туралы ойламайды. Бұл тым күрделі, ал бағдарламашылар жалқау :) Кемшілігі: Жобаның codeтық базасы қосымша сыныптармен күрделене түседі және үйлеспейтін нүктелер көп болса, олардың саны бақыланbyteын өлшемдерге дейін өсуі мүмкін.

Қасбет пен Декоратормен шатастырмау керек

Үстірт қарап шыққаннан кейін адаптерді қасбет және декоратор үлгілерімен шатастыруға болады. Адаптер мен қасбеттің айырмашылығы - қасбет жаңа интерфейсті енгізеді және бүкіл ішкі жүйені қамтиды. Декоратор, адаптерден айырмашылығы, интерфейсті емес, нысанның өзін өзгертеді.

Қадамдық іске асыру алгоритмі

  1. Алдымен, бұл үлгі шеше алатын мәселе бар екеніне көз жеткізіңіз.

  2. Клиент интерфейсін анықтаңыз, оның атынан басқа класс пайдаланылады.

  3. Алдыңғы қадамда анықталған интерфейс негізінде адаптер класын іске асырыңыз.

  4. Адаптер сыныбында нысанға сілтемені сақтайтын өрісті жасаңыз. Бұл сілтеме конструкторда беріледі.

  5. Адаптерде барлық клиенттік интерфейс әдістерін енгізіңіз. Әдіс мүмкін:

    • Қоңырауды өзгертусіз жіберу;

    • Деректерді өзгерту, мақсатты әдіске шақырулар санын көбейту/азайту, деректер құрамын одан әрі кеңейту және т.б.

    • Соңғы шара ретінде, егер белгілі бір әдіс үйлесімсіз болса, қатаң түрде құжатталуы керек UnsupportedOperationException жіберіңіз.

  6. Егер қолданба адаптерді клиент интерфейсі арқылы ғана пайдаланса (жоғарыдағы мысалдағыдай), бұл адаптерлерді болашақта ауыртпалықсыз ұзартуға мүмкіндік береді.

Әрине, дизайн үлгісі барлық аурулардың панацеясы емес, бірақ оның көмегімен әртүрлі интерфейстері бар an objectілердің үйлесімсіздігі мәселесін талғампаз түрде шеше аласыз. Негізгі үлгілерді білетін әзірлеуші ​​алгоритмдерді жазуды білетіндерден бірнеше саты жоғары, өйткені олар маңызды қосымшаларды жасау үшін қажет. Кодты қайта пайдалану қиынға соғады және оны сақтау - рахат. Бүгінге бәрі осы! Бірақ жақын арада біз әртүрлі дизайн үлгілерімен танысуымызды жалғастырамыз :)
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION