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:- Interface
TimetableTrains
. - Ang klase
TimetableElectricTrains
na nagpapatupad ng interface na ito. - Ito ay sa pamamagitan ng klase na ang client code ay nakikipag-ugnayan sa disk file system.
- Klase ng kliyente
DisplayTimetable
. Ang pamamaraan nitoprintTimetable()
ay gumagamit ng mga pamamaraan ng klaseTimetableElectricTrains
.
printTimetable()
ng klase TimetableElectricTrains
ang 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 ng 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. DisplayTimetable
TimetableElectricTrainsProxy
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:- Pag-cache.
- 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?
- Mga kahilingan sa pag-log.
- Pansamantalang data at mga pagsusuri sa pag-access.
- Paglulunsad ng mga parallel processing thread.
- Pagre-record o pagbibilang ng history ng isang tawag.
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:
-
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
. -
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.
-
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.
-
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.
GO TO FULL VERSION