JavaRush /Java блогу /Random-KY /Прокси дизайн үлгүсү

Прокси дизайн үлгүсү

Группада жарыяланган
Программалоодо колдонмонун архитектурасын туура пландаштыруу маанилүү. Бул үчүн ажырагыс куралы дизайн үлгүлөрү болуп саналат. Бүгүн биз прокси, же башкача айтканда, орун басары жөнүндө сүйлөшөбүз.

Депутаттын эмне кереги бар?

Бул үлгү an objectке башкарылуучу кирүү менен байланышкан көйгөйлөрдү чечүүгө жардам берет. Сизде суроо пайда болушу мүмкүн: "Мындай башкарылуучу мүмкүнчүлүк бизге эмне үчүн керек?" Келгиле, эмне экенин түшүнүүгө жардам бере турган бир нече жагдайды карап көрөлү.

Мисал 1

Келгиле, бизде бир топ эски codeдору бар чоң долбоор бар деп элестетип көрөлү, анда маалымат базасынан отчетторду жүктөө үчүн жооптуу класс бар. Класс синхрондуу иштейт, башкача айтканда, маалымат базасы өтүнүчтү иштетип жатканда, бүт система бош турат. Орточо алганда, отчет 30 мүнөттө түзүлөт. Бул өзгөчөлүктөн улам анын жүктөлүшү саат 00:30да башталат жана жетекчorк бул отчетту эртең менен алат. Талдоо учурунда отчет түзүлгөндөн кийин дароо, башкача айтканда, бир сутка ичинде кабыл алуу зарыл экени белгилүү болду. Баштоо убактысын өзгөртүү мүмкүн эмес, анткени система маалымат базасынан жооп күтөт. Чечим өзүнчө жипте жүктөө жана отчетту түзүү менен иштөө принцибин өзгөртүү. Бул чечим системанын демейдегидей иштешине мүмкүндүк берет жана жетекчorк жаңы отчетторду алат. Бирок, бир көйгөй бар: учурдагы codeду кайра жазуу мүмкүн эмес, анткени анын функциялары системанын башка бөлүктөрүндө колдонулат. Бул учурда, сиз депутаттык үлгүнү колдонуу менен ортоңку прокси классты киргизсеңиз болот, ал отчетту жүктөө, башталыш убактысын киргизүү жана өзүнчө жипти ишке киргизүү өтүнүчүн алат. Отчет түзүлгөндө жип өз ишин бүтүрүп, баары бактылуу болот.

Мисал 2

Өнүктүрүү тобу плакат сайтын түзөт. Жаңы окуялар жөнүндө маалыматтарды алуу үчүн алар үчүнчү тараптын кызматына кайрылышат, алар менен өз ара аракеттенүү атайын жабык китепкана аркылуу ишке ашырылат. Иштеп чыгуу учурунда көйгөй пайда болду: үчүнчү тараптын системасы маалыматтарды күнүнө бир жолу жаңыртып турат жана колдонуучу баракчаны жаңылаган сайын ага суроо-талап келип чыгат. Бул көптөгөн суроо-талаптарды жаратат жана кызмат жооп бербей калат. Чечим кызматтын жообун кэштөө жана ар бир кайра жүктөөдө келгендерге сакталган натыйжаны берүү, керек болсо бул кэшти жаңыртуу. Бул учурда, орун басар үлгүсүн колдонуу даяр функцияны өзгөртпөстөн сонун чечим болуп саналат.

Үлгү кантип иштейт

Бул үлгүнү ишке ашыруу үчүн прокси классын түзүшүңүз керек. Ал кардар codeу үчүн анын жүрүм-турумун окшоштурган кызмат класс интерфейсин ишке ашырат. Ошентип, реалдуу an objectтин ордуна кардар прокси менен өз ара аракеттенет. Эреже катары, бардык суроо-талаптар кызмат классына берилет, бирок анын чалуу алдында же андан кийин кошумча аракеттер менен. Жөнөкөй сөз менен айтканда, бул прокси an object кардар codeу менен максаттуу an objectинин ортосундагы катмар болуп саналат. Өтө жай эски дисктен сурамды кэштөө мисалын карап көрөлү. Бул кандайдыр бир байыркы колдонуудагы электр поездинин графиги болсун, анын иштөө принциби өзгөртүлбөйт. Жаңыртылган график менен диск күн сайын белгиленген убакытта киргизилет. Ошентип, бизде:
  1. Interface TimetableTrains.
  2. TimetableElectricTrainsБул интерфейсти ишке ашырган класс .
  3. Дал ушул класс аркылуу кардар codeу дисктин файл системасы менен өз ара аракеттенет.
  4. Кардар классы DisplayTimetable. Анын ыкмасы printTimetable()методдорун колдонот TimetableElectricTrains.
Схема жөнөкөй: Прокси дизайн үлгүсү - 2Учурда ар бир ыкма чакырылганда printTimetable()класс TimetableElectricTrainsдискке кирип, маалыматтарды түшүрүп, аны кардарга берет. Бул система жакшы иштейт, бирок абдан жай иштейт. Ошондуктан, кэштөө механизмин кошуу менен системанын иштешин жогорулатуу чечими кабыл алынды. Муну прокси үлгүсү аркылуу жасаса болот: Прокси дизайн үлгүсү - 3Ошентип класс мурунку DisplayTimetableкласс менен эмес, класс менен өз ара аракеттенип жатканын байкабай калат . TimetableElectricTrainsProxyЖаңы ишке ашыруу графикти күнүнө бир жолу жүктөйт жана кайталанган суроо-талаптар боюнча, буга чейин жүктөлгөн an objectти эстутумдан кайтарат.

Проксиди кандай тапшырмалар үчүн колдонгон жакшы?

Бул жерде бул үлгү сөзсүз пайдалуу боло турган бир нече жагдайлар бар:
  1. Кэштөө.
  2. Жалкоо ишке ашыруу жалкоолук ишке ашыруу катары да белгилүү. Керектүү түрдө жүктөй турган болсоңуз, эмне үчүн an objectти бир убакта жүктөө керек?
  3. Сурамдарды жазуу.
  4. Убактылуу маалыматтар жана мүмкүндүк текшерүүлөр.
  5. Параллель иштетүү жиптерин ишке киргизүү.
  6. Чалуу тарыхын жаздыруу же эсептөө.
Башка колдонуу учурлары да бар. Бул үлгүнүн иштөө принцибин түшүнүү менен, сиз ага ийгorктүү тиркемени таба аласыз. Бир караганда, орун басары ошол эле нерсени кылат Фасад , бирок андай эмес. Прокси тейлөө an objectисиндей эле интерфейске ээ . Ошондой эле, үлгүнү Decorator же Адаптер менен чаташтырбаңыз . Декоратор кеңейтилген интерфейсти камсыз кылат, ал эми Адаптер альтернативалуу интерфейсти камсыз кылат.

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

  • + Сиз каалагандай тейлөө an objectине кирүү мүмкүнчүлүгүн көзөмөлдөй аласыз;
  • + Тейлөө an objectинин жашоо циклин башкаруу үчүн кошумча мүмкүнчүлүктөр;
  • + Тейлөө an objectисиз иштейт;
  • + Коддун иштешин жана коопсуздугун жакшыртат.
  • - кошумча дарылоодон улам иштин начарлашы коркунучу бар;
  • - программалык класстардын структурасын татаалдантат.

Иш жүзүндө алмаштыруу үлгүсү

Келгиле, сиз менен дисктен поезддердин графиктерин окуган системаны ишке ашыралы:
public interface TimetableTrains {
   String[] getTimetable();
   String getTrainDepartureTime();
}
Негизги интерфейсти ишке ашыруучу класс:
public class TimetableElectricTrains implements TimetableTrains {

   @Override
   public String[] getTimetable() {
       ArrayList<String> list = new ArrayList<>();
       try {
           Scanner scanner = new Scanner(new FileReader(new File("/tmp/electric_trains.csv")));
           while (scanner.hasNextLine()) {
               String line = scanner.nextLine();
               list.add(line);
           }
       } catch (IOException e) {
           System.err.println("Error:  " + e);
       }
       return list.toArray(new String[list.size()]);
   }

   @Override
   public String getTrainDepartureTime(String trainId) {
       String[] timetable = getTimetable();
       for(int i = 0; i<timetable.length; i++) {
           if(timetable[i].startsWith(trainId+";")) return timetable[i];
       }
       return "";
   }
}
Бардык поезддердин графигин алууга аракет кылган сайын, программа дисктен файлды окуйт. Бирок булар дагы эле гүлдөр. Файл ошондой эле бир эле поезддин графигин алуу керек болгон сайын окулат! Мындай code жаман мисалдарда гана бар болгону жакшы :) Кардар классы:
public class DisplayTimetable {
   private TimetableTrains timetableTrains = new TimetableElectricTrains();

   public void printTimetable() {
       String[] timetable = timetableTrains.getTimetable();
       String[] tmpArr;
       System.out.println("Поезд\tОткуда\tКуда\t\tВремя отправления\tВремя прибытия\tВремя в пути");
       for(int i = 0; i < timetable.length; i++) {
           tmpArr = timetable[i].split(";");
           System.out.printf("%s\t%s\t%s\t\t%s\t\t\t\t%s\t\t\t%s\n", tmpArr[0], tmpArr[1], tmpArr[2], tmpArr[3], tmpArr[4], tmpArr[5]);
       }
   }
}
Мисал файл:

9B-6854;Лондон;Прага;13:43;21:15;07:32
BA-1404;Париж;Грац;14:25;21:25;07:00
9B-8710;Прага;Вена;04:48;08:49;04:01;
9B-8122;Прага;Грац;04:48;08:49;04:01
сынап көрөлү:
public static void main(String[] args) {
   DisplayTimetable displayTimetable = new DisplayTimetable();
   displayTimetable.printTimetable();
}
Жыйынтык:

Поезд  Откуда  Куда   Время отправления Время прибытия    Время в пути
9B-6854  Лондон  Прага    13:43         21:15         07:32
BA-1404  Париж   Грац   14:25         21:25         07:00
9B-8710  Прага   Вена   04:48         08:49         04:01
9B-8122  Прага   Грац   04:48         08:49         04:01
Эми үлгүбүздү ишке ашыруу кадамдарын карап көрөлү:
  1. Баштапкы an objectтин ордуна жаңы прокси колдонууга мүмкүндүк берген интерфейсти аныктаңыз. Биздин мисалда бул TimetableTrains.

  2. Прокси класс түзүңүз. Ал тейлөө an objectисине шилтемени камтышы керек (класста түзүү же конструктордо өтүү);

    Бул жерде биздин прокси классыбыз:

    public class TimetableElectricTrainsProxy implements TimetableTrains {
       // Ссылка на оригинальный an object
       private TimetableTrains timetableTrains = new TimetableElectricTrains();
    
       private String[] timetableCache = null
    
       @Override
       public String[] getTimetable() {
           return timetableTrains.getTimetable();
       }
    
       @Override
       public String getTrainDepartureTime(String trainId) {
           return timetableTrains.getTrainDepartureTime(trainId);
       }
    
       public void clearCache() {
           timetableTrains = null;
       }
    }

    Бул этапта биз жөн гана баштапкы an objectке шилтеме менен класс түзөбүз жана ага бардык чалууларды өткөрөбүз.

  3. Биз прокси классынын логикасын ишке ашырабыз. Негизи чалуу ар дайым баштапкы an objectке багытталат.

    public class TimetableElectricTrainsProxy implements TimetableTrains {
       // Ссылка на оригинальный an object
       private TimetableTrains timetableTrains = new TimetableElectricTrains();
    
       private String[] timetableCache = null
    
       @Override
       public String[] getTimetable() {
           if(timetableCache == null) {
               timetableCache = timetableTrains.getTimetable();
           }
           return timetableCache;
       }
    
       @Override
       public String getTrainDepartureTime(String trainId) {
           if(timetableCache == null) {
               timetableCache = timetableTrains.getTimetable();
           }
           for(int i = 0; i < timetableCache.length; i++) {
               if(timetableCache[i].startsWith(trainId+";")) return timetableCache[i];
           }
           return "";
       }
    
       public void clearCache() {
           timetableTrains = null;
       }
    }

    Метод getTimetable()график массивинин эстутумда кэштелгендигин текшерет. Болбосо, натыйжаны сактоо менен дисктен маалыматтарды жүктөө өтүнүчүн чыгарат. Эгер суроо-талап мурунтан эле иштеп жаткан болсо, ал an objectти эс тутумдан тез кайтарат.

    Жөнөкөй функционалдуулугунун аркасында getTrainDepartireTime() ыкмасын баштапкы an objectке багыттоо зарыл болгон эмес. Биз жөн гана анын функционалдуулугун жаңы ыкмага көчүрдүк.

    Сен муну кыла албайсың. Эгер сиз codeду кайталашыңыз же окшош манипуляцияларды жасооңуз керек болсо, анда бул бир нерсе туура эмес болуп кетти дегенди билдирет жана көйгөйдү башка бурчтан карашыңыз керек. Биздин жөнөкөй мисалда башка жол жок, бирок реалдуу долбоорлордо, балким, code туурараак жазылат.

  4. Кардар codeундагы баштапкы an objectтин түзүлүшүн алмаштыруучу an object менен алмаштырыңыз:

    public class DisplayTimetable {
       // Измененная link
       private TimetableTrains timetableTrains = new TimetableElectricTrainsProxy();
    
       public void printTimetable() {
           String[] timetable = timetableTrains.getTimetable();
           String[] tmpArr;
           System.out.println("Поезд\tОткуда\tКуда\t\tВремя отправления\tВремя прибытия\tВремя в пути");
           for(int i = 0; i<timetable.length; i++) {
               tmpArr = timetable[i].split(";");
               System.out.printf("%s\t%s\t%s\t\t%s\t\t\t\t%s\t\t\t%s\n", tmpArr[0], tmpArr[1], tmpArr[2], tmpArr[3], tmpArr[4], tmpArr[5]);
           }
       }
    }

    Экспертиза

    
    Поезд  Откуда  Куда   Время отправления Время прибытия    Время в пути
    9B-6854  Лондон  Прага    13:43         21:15         07:32
    BA-1404  Париж   Грац   14:25         21:25         07:00
    9B-8710  Прага   Вена   04:48         08:49         04:01
    9B-8122  Прага   Грац   04:48         08:49         04:01

    Улуу, ал туура иштейт.

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

Чекиттин ордуна пайдалуу шилтеме

  1. Үлгүлөр жөнүндө сонун макала жана "Депутат" жөнүндө бир аз

Бүгүнкү күндө бардыгы ушул! Үйрөнүүгө кайтып барып, жаңы бorмиңизди иш жүзүндө сынап көрсөңүз жакшы болмок :)
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION