JavaRush /Java Blog /Random-TL /Pattern ng disenyo ng proxy

Pattern ng disenyo ng proxy

Nai-publish sa grupo
Sa programming, mahalaga na maayos na planuhin ang arkitektura ng aplikasyon. Ang isang kailangang-kailangan na tool para dito ay mga pattern ng disenyo. Ngayon ay pag-uusapan natin ang tungkol sa Proxy, o sa madaling salita, Deputy.

Bakit kailangan mo ng Deputy?

Ang pattern na ito ay tumutulong sa paglutas ng mga problemang nauugnay sa kontroladong pag-access sa isang bagay. Maaaring mayroon kang tanong: "Bakit kailangan namin ng ganoong kontroladong pag-access?" Tingnan natin ang ilang sitwasyon na makakatulong sa iyong malaman kung ano.

Halimbawa 1

Isipin natin na mayroon tayong malaking proyekto na may isang grupo ng lumang code, kung saan mayroong isang klase na responsable para sa pag-download ng mga ulat mula sa database. Gumagana ang klase nang sabay-sabay, iyon ay, ang buong sistema ay idle habang pinoproseso ng database ang kahilingan. Sa karaniwan, ang isang ulat ay nabuo sa loob ng 30 minuto. Dahil sa feature na ito, magsisimula ang pag-upload nito sa 00:30, at natatanggap ng management ang ulat na ito sa umaga. Sa panahon ng pagsusuri, lumabas na kinakailangan na matanggap kaagad ang ulat pagkatapos na ito ay nabuo, iyon ay, sa loob ng isang araw. Imposibleng muling iiskedyul ang oras ng pagsisimula, dahil maghihintay ang system ng tugon mula sa database. Ang solusyon ay baguhin ang prinsipyo ng pagpapatakbo sa pamamagitan ng pagsisimula ng pag-upload at pagbuo ng ulat sa isang hiwalay na thread. Ang solusyon na ito ay magbibigay-daan sa system na gumana gaya ng dati, at ang pamamahala ay makakatanggap ng mga bagong ulat. Gayunpaman, mayroong isang problema: ang kasalukuyang code ay hindi maaaring muling isulat, dahil ang mga function nito ay ginagamit ng ibang mga bahagi ng system. Sa kasong ito, maaari kang magpakilala ng intermediate proxy class gamit ang Deputy pattern, na makakatanggap ng kahilingang mag-upload ng ulat, mag-log sa oras ng pagsisimula at maglunsad ng hiwalay na thread. Kapag nabuo ang ulat, makukumpleto ng thread ang gawain nito at magiging masaya ang lahat.

Halimbawa 2

Ang development team ay gumagawa ng poster website. Upang makakuha ng data tungkol sa mga bagong kaganapan, bumaling sila sa isang third-party na serbisyo, ang pakikipag-ugnayan na ipinapatupad sa pamamagitan ng isang espesyal na saradong library. Sa panahon ng pag-unlad, lumitaw ang isang problema: ang isang third-party na system ay nag-a-update ng data isang beses sa isang araw, at isang kahilingan para dito ay nangyayari sa tuwing nire-refresh ng user ang pahina. Lumilikha ito ng malaking bilang ng mga kahilingan at huminto sa pagtugon ang serbisyo. Ang solusyon ay i-cache ang tugon sa serbisyo at bigyan ang mga bisita ng naka-save na resulta sa bawat pag-reboot, ina-update ang cache na ito kung kinakailangan. Sa kasong ito, ang paggamit ng Deputy pattern ay isang mahusay na solusyon nang hindi binabago ang natapos na pag-andar.

Paano gumagana ang pattern

Upang ipatupad ang pattern na ito, kailangan mong lumikha ng proxy class. Nagpapatupad ito ng interface ng klase ng serbisyo, na ginagaya ang pag-uugali nito para sa code ng kliyente. Kaya, sa halip na ang tunay na bagay, ang kliyente ay nakikipag-ugnayan sa proxy nito. Karaniwan, ang lahat ng mga kahilingan ay ipinapasa sa klase ng serbisyo, ngunit may mga karagdagang aksyon bago o pagkatapos ng tawag nito. Sa madaling salita, ang proxy object na ito ay isang layer sa pagitan ng client code at ng target na object. Tingnan natin ang isang halimbawa ng pag-cache ng kahilingan mula sa napakabagal na lumang disk. Hayaan itong isang iskedyul ng electric train sa ilang sinaunang aplikasyon, na ang prinsipyo ng pagpapatakbo ay hindi mababago. Ang disk na may na-update na iskedyul ay ipinapasok araw-araw sa isang nakapirming oras. Kaya mayroon kaming:
  1. Interface TimetableTrains.
  2. Ang klase TimetableElectricTrainsna nagpapatupad ng interface na ito.
  3. Ito ay sa pamamagitan ng klase na ang client code ay nakikipag-ugnayan sa disk file system.
  4. Klase ng kliyente DisplayTimetable. Ang pamamaraan nito printTimetable()ay gumagamit ng mga pamamaraan ng klase TimetableElectricTrains.
Ang pamamaraan ay simple: Pattern ng Proxy Design - 2Sa kasalukuyan, sa tuwing tinatawag ang isang pamamaraan, ina-access printTimetable()ng klase TimetableElectricTrainsang disk, ibinababa ang data at ibinibigay ito sa kliyente. Ang sistemang ito ay gumagana nang maayos, ngunit napakabagal. Samakatuwid, napagpasyahan na pataasin ang pagganap ng system sa pamamagitan ng pagdaragdag ng mekanismo ng pag-cache. Magagawa ito gamit ang Proxy pattern: Sa ganitong paraan hindi mapapansin Pattern ng Disenyo ng Proxy - 3ng klase na nakikipag-ugnayan ito sa klase at hindi sa nauna. Nilo-load ng bagong pagpapatupad ang iskedyul isang beses sa isang araw, at sa mga paulit-ulit na kahilingan, ibinabalik ang na-load na bagay mula sa memorya. DisplayTimetableTimetableElectricTrainsProxy

Para sa anong mga gawain mas mahusay na gumamit ng Proxy?

Narito ang ilang mga sitwasyon kung saan tiyak na magiging kapaki-pakinabang ang pattern na ito:
  1. Pag-cache.
  2. Ang tamad na pagpapatupad ay kilala rin bilang tamad na pagpapatupad. Bakit mag-load ng isang bagay nang sabay-sabay kung maaari mo itong i-load kung kinakailangan?
  3. Mga kahilingan sa pag-log.
  4. Pansamantalang data at mga pagsusuri sa pag-access.
  5. Paglulunsad ng mga parallel processing thread.
  6. Pagre-record o pagbibilang ng history ng isang tawag.
Mayroon ding iba pang mga kaso ng paggamit. Ang pag-unawa sa prinsipyo ng pagpapatakbo ng pattern na ito, ikaw mismo ay makakahanap ng isang matagumpay na aplikasyon para dito. Sa unang tingin, ginagawa ni Deputy ang parehong bagay tulad ng Facade , ngunit hindi. Ang Proxy ay may parehong interface ng object ng serbisyo. Gayundin, huwag malito ang pattern sa Dekorador o Adapter . Ang Dekorador ay nagbibigay ng pinahabang interface, habang ang Adapter ay nagbibigay ng alternatibo.

Mga kalamangan at kahinaan

  • + Maaari mong kontrolin ang pag-access sa object ng serbisyo ayon sa gusto mo;
  • + Karagdagang mga kakayahan para sa pamamahala sa ikot ng buhay ng isang bagay ng serbisyo;
  • + Gumagana nang walang object ng serbisyo;
  • + Pinapabuti ang pagganap at seguridad ng code.
  • - May panganib na bumagsak ang pagganap dahil sa mga karagdagang paggamot;
  • - Pinapalubha ang istraktura ng mga klase ng programa.

Palitan ang pattern sa pagsasanay

Magpatupad tayo ng isang sistema sa iyo na nagbabasa ng mga iskedyul ng tren mula sa disk:
public interface TimetableTrains {
   String[] getTimetable();
   String getTrainDepartureTime();
}
Isang klase na nagpapatupad ng pangunahing interface:
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 "";
   }
}
Sa tuwing susubukan mong makuha ang iskedyul ng lahat ng mga tren, binabasa ng programa ang file mula sa disk. Ngunit ito ay mga bulaklak pa rin. Binabasa rin ang file sa tuwing kailangan mong makuha ang iskedyul para sa isang tren lang! Mabuti na ang ganitong code ay umiiral lamang sa mga masamang halimbawa :) Klase ng kliyente:
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]);
       }
   }
}
Halimbawa ng file:

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
Subukan natin:
public static void main(String[] args) {
   DisplayTimetable displayTimetable = new DisplayTimetable();
   displayTimetable.printTimetable();
}
Konklusyon:

Поезд  Откуда  Куда   Время отправления Время прибытия    Время в пути
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
Ngayon, dumaan tayo sa mga hakbang ng pagpapatupad ng aming pattern:
  1. Tukuyin ang isang interface na nagbibigay-daan sa iyong gumamit ng bagong proxy sa halip na ang orihinal na bagay. Sa ating halimbawa ito ay TimetableTrains.

  2. Gumawa ng proxy class. Dapat itong maglaman ng isang reference sa isang object ng serbisyo (lumikha sa isang klase o pumasa sa isang constructor);

    Narito ang aming proxy class:

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

    Sa yugtong ito, lumikha lang kami ng isang klase na may reference sa orihinal na bagay at ipapasa ang lahat ng mga tawag dito.

  3. Ipinapatupad namin ang lohika ng klase ng proxy. Karaniwang ang tawag ay palaging na-redirect sa orihinal na bagay.

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

    Sinusuri ng pamamaraan getTimetable()kung ang array ng iskedyul ay naka-cache sa memorya. Kung hindi, naglalabas ito ng kahilingan na i-load ang data mula sa disk, na iniimbak ang resulta. Kung tumatakbo na ang kahilingan, mabilis itong magbabalik ng isang bagay mula sa memorya.

    Salamat sa simpleng functionality nito, ang getTrainDepartireTime() na paraan ay hindi kailangang i-redirect sa orihinal na object. Nadoble lang namin ang functionality nito sa isang bagong paraan.

    Hindi mo magagawa iyon. Kung kailangan mong i-duplicate ang code o magsagawa ng mga katulad na manipulasyon, nangangahulugan ito na may nangyaring mali at kailangan mong tingnan ang problema mula sa ibang anggulo. Sa aming simpleng halimbawa ay walang ibang paraan, ngunit sa mga totoong proyekto, malamang, ang code ay isusulat nang mas tama.

  4. Palitan ang paglikha ng orihinal na bagay sa code ng kliyente ng isang kapalit na bagay:

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

    Pagsusulit

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

    Mahusay, gumagana ito nang tama.

    Maaari mo ring isaalang-alang ang isang pabrika na gagawa ng parehong orihinal na bagay at isang kapalit na bagay depende sa ilang partikular na kundisyon.

Kapaki-pakinabang na link sa halip na isang tuldok

  1. Napakahusay na artikulo tungkol sa mga pattern at kaunti tungkol sa "Deputy"

Yan lamang para sa araw na ito! Masarap bumalik sa pag-aaral at subukan ang iyong bagong kaalaman sa pagsasanay :)
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION