JavaRush /Java Blogu /Random-AZ /Proksi dizayn nümunəsi

Proksi dizayn nümunəsi

Qrupda dərc edilmişdir
Proqramlaşdırmada tətbiqin arxitekturasını düzgün planlaşdırmaq vacibdir. Bunun üçün əvəzedilməz vasitə dizayn nümunələridir. Bu gün biz Proxy və ya başqa sözlə Deputat haqqında danışacağıq.

Deputat nəyə lazımdır?

Bu nümunə obyektə nəzarət edilən girişlə bağlı problemləri həll etməyə kömək edir. Sualınız ola bilər: "Niyə bizə belə idarə olunan giriş lazımdır?" Nəyin nə olduğunu anlamağa kömək edəcək bir neçə vəziyyətə baxaq.

Misal 1

Təsəvvür edək ki, bir dəstə köhnə kodu olan böyük bir layihəmiz var, burada verilənlər bazasından hesabatların yüklənməsinə cavabdeh olan bir sinif var. Sinf sinxron işləyir, yəni verilənlər bazası sorğunu emal edərkən bütün sistem boşdur. Orta hesabla hesabat 30 dəqiqə ərzində hazırlanır. Bu xüsusiyyətə görə onun yüklənməsi saat 00:30-da başlayır və rəhbərlik bu hesabatı səhər alır. Təhlil zamanı məlum oldu ki, hesabat yaradılandan dərhal sonra, yəni bir gün ərzində qəbul edilməlidir. Başlama vaxtını dəyişdirmək mümkün deyil, çünki sistem verilənlər bazasından cavab gözləyəcək. Həll yolu, yükləmə və hesabat yaratmağa ayrı bir başlıqda başlamaqla iş prinsipini dəyişdirməkdir. Bu həll sistemin həmişəki kimi işləməsinə imkan verəcək və rəhbərlik yeni hesabatlar alacaq. Bununla belə, bir problem var: mövcud kodu yenidən yazmaq mümkün deyil, çünki onun funksiyaları sistemin digər hissələri tərəfindən istifadə olunur. Bu halda, hesabat yükləmək, başlanğıc vaxtını qeyd etmək və ayrı bir mövzu açmaq üçün sorğu alacaq Müavin nümunəsindən istifadə edərək aralıq proxy sinfi təqdim edə bilərsiniz. Hesabat yaradıldıqda ip öz işini tamamlayacaq və hər kəs xoşbəxt olacaq.

Misal 2

İnkişaf qrupu poster veb saytı yaradır. Yeni hadisələr haqqında məlumat əldə etmək üçün onlar üçüncü tərəf xidmətinə müraciət edirlər, onunla qarşılıqlı əlaqə xüsusi qapalı kitabxana vasitəsilə həyata keçirilir. İnkişaf zamanı bir problem yarandı: üçüncü tərəf sistemi məlumatları gündə bir dəfə yeniləyir və istifadəçi səhifəni hər dəfə yenilədikdə ona sorğu baş verir. Bu, çoxlu sayda sorğu yaradır və xidmət cavab verməyi dayandırır. Həll yolu xidmətin cavabını keşləmək və ziyarətçilərə hər yenidən yükləmədə yadda qalan nəticəni təqdim etmək, lazım gəldikdə bu keşi yeniləməkdir. Bu vəziyyətdə, Müavin nümunəsindən istifadə, bitmiş funksionallığı dəyişdirmədən əla bir həlldir.

Nümunə necə işləyir

Bu nümunəni həyata keçirmək üçün bir proxy sinfi yaratmalısınız. O, müştəri kodu üçün davranışını simulyasiya edən xidmət sinfi interfeysini həyata keçirir. Beləliklə, müştəri real obyektin əvəzinə onun proxy ilə qarşılıqlı əlaqədə olur. Tipik olaraq, bütün sorğular xidmət sinfinə ötürülür, lakin onun çağırışından əvvəl və ya sonra əlavə tədbirlərlə. Sadə dillə desək, bu proksi obyekt müştəri kodu ilə hədəf obyekt arasında bir təbəqədir. Çox yavaş köhnə diskdən sorğunun keşləşdirilməsi nümunəsinə baxaq. İş prinsipi dəyişdirilə bilməyən bəzi qədim tətbiqlərdə elektrik qatarlarının cədvəli olsun. Yenilənmiş cədvəli olan disk hər gün müəyyən edilmiş vaxtda daxil edilir. Beləliklə, bizdə:
  1. İnterfeys TimetableTrains.
  2. TimetableElectricTrainsBu interfeysi həyata keçirən sinif .
  3. Məhz bu sinif vasitəsilə müştəri kodu disk fayl sistemi ilə qarşılıqlı əlaqədə olur.
  4. Müştəri sinfi DisplayTimetable. Onun metodu printTimetable()metodlarından istifadə edir TimetableElectricTrains.
Sxem sadədir: Proksi Dizayn Modeli - 2Hal-hazırda metod hər dəfə çağırılanda printTimetable()sinif TimetableElectricTrainsdiskə daxil olur, məlumatları boşaldır və müştəriyə təqdim edir. Bu sistem yaxşı işləyir, lakin çox yavaşdır. Buna görə də, keşləmə mexanizmini əlavə etməklə sistem performansını artırmaq qərara alındı. Bu, Proksi nümunəsindən istifadə etməklə edilə bilər: Proksi Dizayn Modeli - 3Bu yolla sinif əvvəlki ilə deyil, DisplayTimetablesiniflə qarşılıqlı əlaqədə olduğunu belə hiss etməyəcək . TimetableElectricTrainsProxyYeni tətbiq cədvəli gündə bir dəfə yükləyir və təkrar sorğular zamanı artıq yüklənmiş obyekti yaddaşdan qaytarır.

Proksidən hansı vəzifələr üçün istifadə etmək daha yaxşıdır?

Bu nümunənin mütləq faydalı olacağı bir neçə vəziyyət var:
  1. Keşləmə.
  2. Tənbəl həyata keçirmə tənbəl tətbiq kimi də tanınır. Ehtiyac olduqda onu yükləyə bildiyiniz halda, niyə bir obyekti birdən yükləyin?
  3. Giriş sorğuları.
  4. Aralıq məlumatlar və giriş yoxlamaları.
  5. Paralel emal iplərinin işə salınması.
  6. Zəng tarixçəsinin qeyd edilməsi və ya sayılması.
Digər istifadə halları da var. Bu nümunənin işləmə prinsipini başa düşərək, özünüz üçün uğurlu bir tətbiq tapa bilərsiniz. İlk baxışdan deputat Fasad ilə eyni şeyi edir , amma belə deyil. Proksi xidmət obyekti ilə eyni interfeysə malikdir . Həmçinin, naxışı Dekorator və ya Adapter ilə qarışdırmayın . Dekorator genişləndirilmiş interfeys, Adapter isə alternativ interfeys təqdim edir.

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

  • + Xidmət obyektinə girişi istədiyiniz kimi idarə edə bilərsiniz;
  • + Xidmət obyektinin həyat dövrünü idarə etmək üçün əlavə imkanlar;
  • + Xidmət obyekti olmadan işləyir;
  • + Kod performansını və təhlükəsizliyini artırır.
  • - Əlavə müalicələrə görə performansın pisləşməsi riski var;
  • - Proqram dərslərinin strukturunu çətinləşdirir.

Təcrübədə əvəzedici nümunə

Gəlin sizinlə diskdən qatar cədvəllərini oxuyan bir sistem tətbiq edək:
public interface TimetableTrains {
   String[] getTimetable();
   String getTrainDepartureTime();
}
Əsas interfeysi həyata keçirən sinif:
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 "";
   }
}
Hər dəfə bütün qatarların cədvəlini almağa çalışdığınız zaman proqram diskdən faylı oxuyur. Ancaq bunlar hələ də çiçəklərdir. Fayl yalnız bir qatarın cədvəlini əldə etmək üçün hər dəfə oxunur! Belə kodun yalnız pis nümunələrdə olması yaxşıdır :) Müştəri sinfi:
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]);
       }
   }
}
Nümunə fayl:

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
test edək:
public static void main(String[] args) {
   DisplayTimetable displayTimetable = new DisplayTimetable();
   displayTimetable.printTimetable();
}
Nəticə:

Поезд  Откуда  Куда   Время отправления Время прибытия    Время в пути
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
İndi nümunəmizi həyata keçirmək üçün addımlardan keçək:
  1. Orijinal obyekt əvəzinə yeni proksi istifadə etməyə imkan verən interfeys təyin edin. Bizim nümunəmizdə belədir TimetableTrains.

  2. Proksi sinfi yaradın. O, xidmət obyektinə istinaddan ibarət olmalıdır (sinifdə yaradın və ya konstruktorda keçin);

    Budur bizim proxy sinifimiz:

    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;
       }
    }

    Bu mərhələdə biz sadəcə olaraq orijinal obyektə istinadla sinif yaradırıq və bütün zəngləri ona ötürürük.

  3. Proxy sinfinin məntiqini həyata keçiririk. Əsasən zəng həmişə orijinal obyektə yönləndirilir.

    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;
       }
    }

    Metod getTimetable()cədvəl massivinin yaddaşda saxlanıb saxlanmadığını yoxlayır. Əgər yoxsa, o, nəticəni saxlayaraq diskdən məlumat yükləmək üçün sorğu verir. Əgər sorğu artıq işləyirsə, o, tez yaddaşdan obyekti qaytaracaq.

    Sadə funksionallığı sayəsində getTrainDepartireTime() metodunu orijinal obyektə yönləndirmək lazım deyildi. Biz sadəcə onun funksionallığını yeni bir üsula təkrarladıq.

    Siz bunu edə bilməzsiniz. Əgər kodun dublikatını çıxartmalı və ya oxşar manipulyasiyalar etməlisinizsə, bu, nəyinsə səhv getdiyini və problemə başqa bucaqdan baxmaq lazım olduğunu bildirir. Sadə nümunəmizdə başqa yol yoxdur, amma real layihələrdə çox güman ki, kod daha düzgün yazılacaq.

  4. Müştəri kodunda orijinal obyektin yaradılmasını əvəzedici obyektlə əvəz edin:

    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]);
           }
       }
    }

    İmtahan

    
    Поезд  Откуда  Куда   Время отправления Время прибытия    Время в пути
    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

    Əla, düzgün işləyir.

    Müəyyən şərtlərdən asılı olaraq həm orijinal obyekt, həm də əvəzedici obyekt yaradacaq bir fabrik də nəzərdən keçirə bilərsiniz.

Nöqtə əvəzinə faydalı keçid

  1. Nümunələr haqqında əla məqalə və bir az "Müavin" haqqında

Bu gün üçün hamısı budur! Öyrənməyə qayıtmaq və yeni biliklərinizi təcrübədə sınamaq yaxşı olardı :)
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION