CodeGym /Java Blog /Random /Pattern ng disenyo ng proxy
John Squirrels
Antas
San Francisco

Pattern ng disenyo ng proxy

Nai-publish sa grupo
Sa programming, mahalagang planuhin nang tama ang arkitektura ng iyong application. Ang mga pattern ng disenyo ay isang kailangang-kailangan na paraan upang magawa ito. Ngayon ay pag-usapan natin ang tungkol sa mga proxy.

Bakit kailangan mo ng proxy?

Ang pattern na ito ay tumutulong sa paglutas ng mga problemang nauugnay sa kontroladong pag-access sa isang bagay. Maaari mong itanong, "Bakit kailangan natin ng kontroladong pag-access?" Tingnan natin ang ilang sitwasyon na makakatulong sa iyong malaman kung ano.

Halimbawa 1

Isipin na mayroon kaming isang malaking proyekto na may isang bungkos ng lumang code, kung saan mayroong isang klase na responsable para sa pag-export ng mga ulat mula sa isang database. Sabay-sabay na gumagana ang klase. Iyon ay, ang buong sistema ay idle habang pinoproseso ng database ang kahilingan. Sa karaniwan, tumatagal ng 30 minuto upang makabuo ng isang ulat. Alinsunod dito, magsisimula ang proseso ng pag-export sa 12:30 AM, at natatanggap ng management ang ulat sa umaga. Ang isang pag-audit ay nagsiwalat na mas mainam na agad na matanggap ang ulat sa mga normal na oras ng negosyo. Ang oras ng pagsisimula ay hindi maaaring ipagpaliban, at ang system ay hindi maaaring harangan habang naghihintay ito ng tugon mula sa database. Ang solusyon ay baguhin kung paano gumagana ang system, pagbuo at pag-export ng ulat sa isang hiwalay na thread. Ang solusyon na ito ay hahayaan ang system na gumana gaya ng dati, at ang pamamahala ay makakatanggap ng mga bagong ulat. gayunpaman, may problema: ang kasalukuyang code ay hindi maaaring muling isulat, dahil ginagamit ng ibang bahagi ng system ang functionality nito. Sa kasong ito, maaari naming gamitin ang proxy pattern upang ipakilala ang isang intermediate proxy class na makakatanggap ng mga kahilingan para mag-export ng mga ulat, mag-log sa oras ng pagsisimula, at maglunsad ng hiwalay na thread. Kapag nabuo na ang ulat, matatapos ang thread at masaya ang lahat.

Halimbawa 2

Gumagawa ang isang development team ng isang website ng mga kaganapan. Upang makakuha ng data sa mga bagong kaganapan, ang koponan ay nagtatanong ng isang third-party na serbisyo. Pinapadali ng isang espesyal na pribadong aklatan ang pakikipag-ugnayan sa serbisyo. Sa panahon ng pag-unlad, natuklasan ang isang problema: ina-update ng system ng third-party ang data nito isang beses sa isang araw, ngunit isang kahilingan ang ipinapadala dito sa tuwing nagre-refresh ang isang user ng isang pahina. Lumilikha ito ng malaking bilang ng mga kahilingan, at huminto sa pagtugon ang serbisyo. Ang solusyon ay i-cache ang tugon ng serbisyo at ibalik ang naka-cache na resulta sa mga bisita habang nire-reload ang mga pahina, ina-update ang cache kung kinakailangan. Sa kasong ito, ang pattern ng disenyo ng proxy ay isang mahusay na solusyon na hindi nagbabago sa kasalukuyang functionality.

Ang prinsipyo sa likod ng pattern ng disenyo

Upang ipatupad ang pattern na ito, kailangan mong lumikha ng proxy class. Ipinapatupad nito ang interface ng klase ng serbisyo, na ginagaya ang pag-uugali nito para sa code ng kliyente. Sa ganitong paraan, nakikipag-ugnayan ang kliyente sa isang proxy sa halip na sa totoong bagay. Bilang isang panuntunan, ang lahat ng mga kahilingan ay ipinapasa sa klase ng serbisyo, ngunit may mga karagdagang aksyon bago o pagkatapos. Sa madaling salita, ang proxy ay isang layer sa pagitan ng client code at ng target na object. Isaalang-alang ang halimbawa ng pag-cache ng mga resulta ng query mula sa isang luma at napakabagal na hard disk. Ipagpalagay na pinag-uusapan natin ang tungkol sa isang timetable para sa mga de-kuryenteng tren sa ilang sinaunang app na ang lohika ay hindi mababago. Ang isang disk na may na-update na timetable ay ipinapasok araw-araw sa isang nakapirming oras. Kaya, mayroon kaming:
  1. TrainTimetableinterface.
  2. ElectricTrainTimetable, na nagpapatupad ng interface na ito.
  3. Nakikipag-ugnayan ang code ng kliyente sa file system sa pamamagitan ng klase na ito.
  4. TimetableDisplayklase ng kliyente. Ang pamamaraan nito printTimetable()ay gumagamit ng mga pamamaraan ng ElectricTrainTimetableklase.
Ang diagram ay simple: Pattern ng disenyo ng proxy: - 2Sa kasalukuyan, sa bawat tawag ng printTimetable()pamamaraan, ElectricTrainTimetableina-access ng klase ang disk, nilo-load ang data, at inilalahad ito sa kliyente. Ang sistema ay gumagana nang maayos, ngunit ito ay napakabagal. Bilang resulta, ang desisyon ay ginawa upang taasan ang pagganap ng system sa pamamagitan ng pagdaragdag ng mekanismo ng pag-cache. Magagawa ito gamit ang proxy pattern: Pattern ng disenyo ng proxy: - 3Kaya, TimetableDisplayhindi napapansin ng klase na nakikipag-ugnayan ito sa ElectricTrainTimetableProxyklase sa halip na sa lumang klase. Nilo-load ng bagong pagpapatupad ang timetable isang beses sa isang araw. Para sa mga paulit-ulit na kahilingan, ibinabalik nito ang dating na-load na bagay mula sa memorya.

Anong mga gawain ang pinakamainam para sa isang proxy?

Narito ang ilang sitwasyon kung saan tiyak na magiging kapaki-pakinabang ang pattern na ito:
  1. Pag-cache
  2. Naantala, o tamad, ang pagsisimula Bakit mag-load kaagad ng isang bagay kung maaari mo itong i-load kung kinakailangan?
  3. Mga kahilingan sa pag-log
  4. Intermediate na pag-verify ng data at pag-access
  5. Pagsisimula ng mga thread ng manggagawa
  6. Pag-record ng access sa isang bagay
At mayroon ding iba pang mga kaso ng paggamit. Ang pag-unawa sa prinsipyo sa likod ng pattern na ito, maaari mong tukuyin ang mga sitwasyon kung saan matagumpay itong mailalapat. Sa unang tingin, ang isang proxy ay gumagawa ng parehong bagay bilang isang facade , ngunit hindi iyon ang kaso. Ang isang proxy ay may parehong interface ng object ng serbisyo. Gayundin, huwag malito ang pattern na ito sa mga pattern ng dekorador o Adapter . Ang isang dekorador ay nagbibigay ng pinahabang interface, at ang isang adaptor ay nagbibigay ng alternatibong interface.

Mga kalamangan at kahinaan

  • + Maaari mong kontrolin ang pag-access sa object ng serbisyo gayunpaman gusto mo
  • + Mga karagdagang kakayahan na nauugnay sa pamamahala sa ikot ng buhay ng object ng serbisyo
  • + Gumagana ito nang walang object ng serbisyo
  • + Pinapabuti nito ang pagganap at seguridad ng code.
  • - May panganib na maaaring lumala ang performance dahil sa mga karagdagang kahilingan
  • - Ginagawa nitong mas kumplikado ang hierarchy ng klase

Ang proxy pattern sa pagsasanay

Magpatupad tayo ng system na nagbabasa ng mga timetable ng tren mula sa isang hard disk:

public interface TrainTimetable {
   String[] getTimetable();
   String getTrainDepartureTime();
}
Narito ang klase na nagpapatupad ng pangunahing interface:

public class ElectricTrainTimetable implements TrainTimetable {

   @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 bawat oras na makuha mo ang timetable ng tren, ang programa ay nagbabasa ng isang file mula sa disk. Ngunit iyon lamang ang simula ng aming mga problema. Ang buong file ay binabasa sa tuwing makukuha mo ang timetable para sa kahit isang tren! Mabuti na ang ganitong code ay umiiral lamang sa mga halimbawa ng hindi dapat gawin :) Klase ng kliyente:

public class TimetableDisplay {
   private TrainTimetable trainTimetable = new ElectricTrainTimetable();

   public void printTimetable() {
       String[] timetable = trainTimetable.getTimetable();
       String[] tmpArr;
       System.out.println("Train\\tFrom\\tTo\\t\\tDeparture time\\tArrival time\\tTravel time");
       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;London;Prague;13:43;21:15;07:32
BA-1404;Paris;Graz;14:25;21:25;07:00
9B-8710;Prague;Vienna;04:48;08:49;04:01;
9B-8122;Prague;Graz;04:48;08:49;04:01
Subukan natin ito:

public static void main(String[] args) {
   TimetableDisplay timetableDisplay = new timetableDisplay();
   timetableDisplay.printTimetable();
}
Output:

Train  From  To  Departure time  Arrival time  Travel time
9B-6854  London  Prague  13:43  21:15  07:32
BA-1404  Paris  Graz  14:25  21:25  07:00
9B-8710  Prague  Vienna  04:48  08:49  04:01
9B-8122  Prague  Graz  04:48  08:49  04:01
Ngayon, lakad tayo sa mga hakbang na kinakailangan upang ipakilala ang ating pattern:
  1. Tukuyin ang isang interface na nagbibigay-daan sa paggamit ng isang proxy sa halip na ang orihinal na bagay. Sa aming halimbawa, ito ay TrainTimetable.

  2. Lumikha ng klase ng proxy. Dapat itong magkaroon ng reference sa object ng serbisyo (lumikha ito sa klase o ipasa sa constructor).

    Narito ang aming proxy class:

    
    public class ElectricTrainTimetableProxy implements TrainTimetable {
       // Reference to the original object
       private TrainTimetable trainTimetable = new ElectricTrainTimetable();
      
       private String[] timetableCache = null
    
       @Override
       public String[] getTimetable() {
           return trainTimetable.getTimetable();
       }
    
       @Override
       public String getTrainDepartureTime(String trainId) {
           return trainTimetable.getTrainDepartureTime(trainId);
       }
      
       public void clearCache() {
           trainTimetable = null;
       }
    }
    

    Sa yugtong ito, gumagawa lang kami ng klase na may reference sa orihinal na bagay at pinapasa ang lahat ng tawag dito.

  3. Ipatupad natin ang logic ng proxy class. Karaniwan, ang mga tawag ay palaging na-redirect sa orihinal na bagay.

    
    public class ElectricTrainTimetableProxy implements TrainTimetable {
       // Reference to the original object
       private TrainTimetable trainTimetable = new ElectricTrainTimetable();
    
       private String[] timetableCache = null
    
       @Override
       public String[] getTimetable() {
           if (timetableCache == null) {
               timetableCache = trainTimetable.getTimetable();
           }
           return timetableCache;
       }
    
       @Override
       public String getTrainDepartureTime(String trainId) {
           if (timetableCache == null) {
               timetableCache = trainTimetable.getTimetable();
           }
           for (int i = 0; i < timetableCache.length; i++) {
               if (timetableCache[i].startsWith(trainId+";")) return timetableCache[i];
           }
           return "";
       }
    
       public void clearCache() {
           trainTimetable = null;
       }
    }
    

    Ang getTimetable()mga pagsusuri kung ang array ng timetable ay naka-cache sa memorya. Kung hindi, nagpapadala ito ng kahilingan na i-load ang data mula sa disk at i-save ang resulta. Kung hiniling na ang timetable, mabilis nitong ibabalik ang bagay mula sa memorya.

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

    Huwag gawin ito. Kung kailangan mong i-duplicate ang code o gumawa ng katulad na bagay, may nangyaring mali, at kailangan mong tingnan muli ang problema mula sa ibang anggulo. Sa aming simpleng halimbawa, wala kaming ibang pagpipilian. Ngunit sa mga totoong proyekto, ang code ay malamang na maisusulat nang mas tama.

  4. Sa client code, lumikha ng proxy object sa halip na ang orihinal na object:

    
    public class TimetableDisplay {
       // Changed reference
       private TrainTimetable trainTimetable = new ElectricTrainTimetableProxy();
    
       public void printTimetable() {
           String[] timetable = trainTimetable.getTimetable();
           String[] tmpArr;
           System.out.println("Train\\tFrom\\tTo\\t\\tDeparture time\\tArrival time\\tTravel time");
           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]);
           }
       }
    }
    

    Suriin

    
    Train  From  To  Departure time  Arrival time  Travel time
    9B-6854  London  Prague  13:43  21:15  07:32
    BA-1404  Paris  Graz  14:25  21:25  07:00
    9B-8710  Prague  Vienna  04:48  08:49  04:01
    9B-8122  Prague  Graz  04:48  08:49  04:01
    

    Mahusay, gumagana ito nang tama.

    Maaari mo ring isaalang-alang ang opsyon ng isang pabrika na lumilikha ng parehong orihinal na bagay at isang proxy na bagay, depende sa ilang partikular na kundisyon.

Bago tayo magpaalam, narito ang isang kapaki-pakinabang na link

Yan lamang para sa araw na ito! Hindi masamang ideya na bumalik sa mga aralin at subukan ang iyong bagong kaalaman sa pagsasanay :)
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION