JavaRush /Java блогу /Random-KY /Адаптердин дизайны кандай көйгөйлөрдү чечет?

Адаптердин дизайны кандай көйгөйлөрдү чечет?

Группада жарыяланган
Программалык камсыздоону иштеп чыгуу көбүнчө бири-бири менен иштеген компоненттердин бири-бирине дал келбегендигинен улам татаалдашат. Мисалы, жаңы китепкананы Javaнын мурунку versionларында жазылган эски платформа менен интеграциялоо керек болсо, an objectтердин, тагыраак айтканда интерфейстердин дал келбегендигине туш болушуңуз мүмкүн. Бул учурда эмне кылуу керек? Кодду кайра жазасызбы? Бирок бул мүмкүн эмес: системаны талдоо көп убакытты талап кылат же иштин ички логикасы бузулат. Адаптердин дизайны кандай көйгөйлөрдү чечет - 1Бул көйгөйдү чечүү үчүн алар бири-бирине туура келбеген интерфейстери бар an objectтердин чогуу иштешине жардам берген Адаптер үлгүсүн ойлоп табышты. Келгиле, аны кантип колдонууну карап көрөлү!

Көйгөй тууралуу көбүрөөк маалымат

Биринчиден, эски системанын жүрүм-турумун окшоштуралы. Бул жумушка же мектепке кечигип калуу себептерин жаратат дейли. Бул үчүн бизде , жана ыкмаларын 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 дизайн үлгүсү кандай көйгөйлөрдү чечет?Системанын бул versionсы Excuse интерфейси менен гана иштейт. Кодду кайра жаза албайсыз: чоң тиркемеде мындай өзгөртүүлөр көп убакытка созулушу же колдонмонун логикасын бузушу мүмкүн. Сиз негизги интерфейсти киргизүүнү жана иерархияны көбөйтүүнү сунуштай аласыз: Адаптердин дизайны кандай көйгөйлөрдү чечет - 3Бул үчүн интерфейстин атын өзгөртүү керек Excuse. Бирок олуттуу колдонмолордо кошумча иерархия жагымсыз: жалпы тамыр элементин киргизүү архитектураны бузат. Сиз жаңы жана эски функцияларды минималдуу жоготуу менен колдонууга мүмкүндүк берүүчү орто классты ишке ашырышыңыз керек. Кыскасы, сизге адаптер керек .

Адаптердин үлгүсү кантип иштейт

Адаптер - бул бир an object боюнча методдорго чалууларды экинчисине түшүнүктүү кылган аралык an object. Биздин мисал үчүн адаптерди ишке ашырып, аны чакыралы Middleware. Биздин адаптер an objectтердин бирине шайкеш келген интерфейсти ишке ашырышы керек. Ошондой болсун Excuse. Мунун аркасында Middlewareбиринчи an objectтин ыкмаларын чакыра алат. Middlewareчалууларды кабыл алат жана аларды шайкеш форматта экинчи an objectке өткөрүп берет. 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чалууну кошумча трансформациясыз башка an objectке өткөрүп берет. Метод dislikeExcuseадегенде шылтоону маалымат базасынын кара тизмесине киргизүүнү талап кылды. Кошумча аралык маалымат иштетүү Адаптер үлгүсү эмне үчүн сүйүктүү себеби болуп саналат. likeExcuseБирок интерфейсте Excuseжок, бирок интерфейсте болгон ыкма жөнүндө эмне айтууга болот StudentExcuse? Бул операция жаңы функцияда колдоого алынbyte. Мындай учурда, алар бир өзгөчөлүк менен келишти 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.
   }
}
Бир караганда, бул чечим ийгorктүү көрүнбөйт, бирок функцияны симуляциялоо татаалыраак кырдаалга алып келиши мүмкүн. кардар кунт коюп жана адаптер жакшы documentтештирилген болсо, бул чечим кабыл алынат.

Адаптерди качан колдонуу керек

  1. Эгер сиз үчүнчү жактын классын колдонушуңуз керек болсо, бирок анын интерфейси негизги тиркеме менен шайкеш келбейт. Жогорудагы мисалда чалууларды максаттуу an object үчүн түшүнүктүү форматта ороп турган чип an objectинин кантип түзүлгөнү көрсөтүлгөн.

  2. Качан бир нече бар подкласстар жалпы функцияга ээ болушу керек. Кошумча класстардын ордуна (алардын түзүлүшү codeдун кайталанышына алып келет), адаптерди колдонуу жакшы.

Артыкчылыктары жана кемчorктери

Артыкчылыгы: Адаптер кардардан бир an objectтен экинчисине суроо-талаптарды иштетүү деталдарын жашырат. Кардар codeу маалыматтарды форматтоо же максаттуу ыкмага чалууларды башкаруу жөнүндө ойлонбойт. Бул өтө татаал, ал эми программисттер жалкоо :) Кемчorги: Долбоордун codeдук базасы кошумча класстар менен татаалданган, ал эми бири-бирине дал келбеген пункттардын саны көп болсо, алардын саны башкарылгыс өлчөмдөргө чейин өсүшү мүмкүн.

Фасад жана жасалгалоочу менен чаташтырбоо керек

Үстүртөн кароодон кийин, Адаптерди Фасад жана Декоратор үлгүлөрү менен чаташтырууга болот. Адаптер менен Фасаддын айырмасы, Фасад жаңы интерфейсти киргизип, бүтүндөй подсистеманы камтыйт. Декоратор, адаптерден айырмаланып, интерфейсти эмес, an objectтин өзүн өзгөртөт.

Этап-этабы менен ишке ашыруу алгоритми

  1. Биринчиден, бул үлгү чече ала турган көйгөй бар экенин текшериңиз.

  2. Башка класс колдонула турган кардар интерфейсин аныктаңыз.

  3. Мурунку кадамда аныкталган интерфейстин негизинде адаптер классын ишке ашырыңыз.

  4. Адаптер классында an objectке шилтемени сактаган талааны түзүңүз. Бул шилтеме конструктордо өткөрүлөт.

  5. Адаптерде кардар интерфейсинин бардык ыкмаларын ишке ашырыңыз. Метод болот:

    • Чалууну өзгөртүүсүз өткөрүү;

    • Маалыматтарды өзгөртүү, максаттуу ыкмага чалуулардын санын көбөйтүү/азайтуу, маалыматтардын курамын андан ары кеңейтүү ж.б.

    • Акыркы чара катары, эгер белгилүү бир ыкма шайкеш келбесе, UnsupportedOperationException ыргытыңыз, ал катуу documentтештирorши керек.

  6. Эгерде тиркеме адаптерди кардар интерфейси аркылуу гана колдонсо (жогоруда көрсөтүлгөндөй), бул адаптерлерди келечекте оорутпай узартууга мүмкүндүк берет.

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