तुम्हाला प्रॉक्सीची गरज का आहे?
हा नमुना ऑब्जेक्टवर नियंत्रित प्रवेशाशी संबंधित समस्यांचे निराकरण करण्यात मदत करतो. तुम्ही विचारू शकता, "आम्हाला नियंत्रित प्रवेशाची गरज का आहे?" चला काही परिस्थिती पाहू ज्या तुम्हाला काय आहे हे समजण्यास मदत करतील.उदाहरण १
कल्पना करा की आमच्याकडे जुन्या कोडचा एक समूह असलेला एक मोठा प्रकल्प आहे, जिथे डेटाबेसमधून अहवाल निर्यात करण्यासाठी जबाबदार वर्ग आहे. वर्ग समकालिकपणे कार्य करतो. म्हणजेच, डेटाबेस विनंतीवर प्रक्रिया करत असताना संपूर्ण प्रणाली निष्क्रिय असते. सरासरी, अहवाल तयार करण्यासाठी 30 मिनिटे लागतात. त्यानुसार, निर्यात प्रक्रिया सकाळी 12:30 वाजता सुरू होते आणि व्यवस्थापनाला सकाळी अहवाल प्राप्त होतो. ऑडिटमध्ये असे दिसून आले की सामान्य कामकाजाच्या वेळेत अहवाल त्वरित प्राप्त करणे अधिक चांगले होईल. प्रारंभ वेळ पुढे ढकलला जाऊ शकत नाही, आणि डेटाबेसच्या प्रतिसादाची प्रतीक्षा करत असताना सिस्टम अवरोधित करू शकत नाही. वेगळ्या धाग्यावर अहवाल तयार करणे आणि निर्यात करणे ही प्रणाली कशी कार्य करते ते बदलणे हा उपाय आहे. हे समाधान प्रणालीला नेहमीप्रमाणे कार्य करू देईल आणि व्यवस्थापनाला नवीन अहवाल प्राप्त होतील. तथापि, एक समस्या आहे: वर्तमान कोड पुन्हा लिहिला जाऊ शकत नाही, कारण सिस्टमचे इतर भाग त्याची कार्यक्षमता वापरतात. या प्रकरणात, आम्ही इंटरमीडिएट प्रॉक्सी क्लास सादर करण्यासाठी प्रॉक्सी पॅटर्न वापरू शकतो जे अहवाल निर्यात करण्यासाठी, प्रारंभ वेळ लॉग करण्यासाठी आणि वेगळा थ्रेड लाँच करण्यासाठी विनंत्या प्राप्त करेल. एकदा रिपोर्ट जनरेट झाला की, धागा संपतो आणि सगळे खुश होतात.उदाहरण २
विकास कार्यसंघ इव्हेंट वेबसाइट तयार करत आहे. नवीन इव्हेंट्सचा डेटा मिळविण्यासाठी, टीम तृतीय-पक्ष सेवेची चौकशी करते. एक विशेष खाजगी लायब्ररी सेवेसह परस्परसंवाद सुलभ करते. विकासादरम्यान, एक समस्या आढळली: तृतीय-पक्ष प्रणाली दिवसातून एकदा त्याचा डेटा अद्यतनित करते, परंतु प्रत्येक वेळी वापरकर्त्याने पृष्ठ रीफ्रेश केल्यावर त्यास विनंती पाठविली जाते. यामुळे मोठ्या संख्येने विनंत्या तयार होतात आणि सेवा प्रतिसाद देणे थांबवते. सेवेचा प्रतिसाद कॅशे करणे आणि पृष्ठे रीलोड झाल्यावर कॅशे अद्ययावत करून अभ्यागतांना कॅशे केलेले निकाल परत करणे हा उपाय आहे. या प्रकरणात, प्रॉक्सी डिझाइन नमुना हा एक उत्कृष्ट उपाय आहे जो विद्यमान कार्यक्षमता बदलत नाही.डिझाइन पॅटर्नमागील तत्त्व
या पॅटर्नची अंमलबजावणी करण्यासाठी, तुम्हाला प्रॉक्सी क्लास तयार करणे आवश्यक आहे. हे क्लायंट कोडसाठी त्याच्या वर्तनाची नक्कल करून सेवा वर्गाचा इंटरफेस लागू करते. अशा प्रकारे, क्लायंट वास्तविक ऑब्जेक्टऐवजी प्रॉक्सीशी संवाद साधतो. नियमानुसार, सर्व विनंत्या सेवा वर्गाकडे पाठविल्या जातात, परंतु आधी किंवा नंतर अतिरिक्त क्रियांसह. सोप्या भाषेत सांगायचे तर, प्रॉक्सी हा क्लायंट कोड आणि लक्ष्य ऑब्जेक्टमधील एक स्तर आहे. जुन्या आणि अतिशय मंद हार्ड डिस्कवरून कॅशिंग क्वेरी परिणामांचे उदाहरण विचारात घ्या. समजा आपण काही प्राचीन अॅपमध्ये इलेक्ट्रिक ट्रेनच्या वेळापत्रकाबद्दल बोलत आहोत ज्याचे तर्क बदलले जाऊ शकत नाहीत. अद्ययावत वेळापत्रक असलेली डिस्क दररोज एका ठराविक वेळी घातली जाते. तर, आमच्याकडे आहे:TrainTimetable
इंटरफेसElectricTrainTimetable
, जे हा इंटरफेस लागू करते.- क्लायंट कोड या वर्गाद्वारे फाइल सिस्टमशी संवाद साधतो.
TimetableDisplay
ग्राहक वर्ग. त्याचीprintTimetable()
पद्धत वर्गाच्या पद्धती वापरतेElectricTrainTimetable
.
printTimetable()
, ElectricTrainTimetable
वर्ग डिस्कवर प्रवेश करतो, डेटा लोड करतो आणि क्लायंटला सादर करतो. प्रणालीचे कार्य ठीक आहे, परंतु ते खूप मंद आहे. परिणामी, कॅशिंग यंत्रणा जोडून सिस्टम कार्यप्रदर्शन वाढविण्याचा निर्णय घेण्यात आला. हे प्रॉक्सी पॅटर्न वापरून केले जाऊ शकते: अशा प्रकारे, TimetableDisplay
वर्गाच्या लक्षातही येत नाही की तो ElectricTrainTimetableProxy
जुन्या वर्गाऐवजी वर्गाशी संवाद साधत आहे. नवीन अंमलबजावणी दिवसातून एकदा वेळापत्रक लोड करते. पुनरावृत्ती विनंतीसाठी, ते मेमरीमधून पूर्वी लोड केलेले ऑब्जेक्ट परत करते.
प्रॉक्सीसाठी कोणती कार्ये सर्वोत्तम आहेत?
येथे काही परिस्थिती आहेत जेथे हा नमुना निश्चितपणे उपयुक्त ठरेल:- कॅशिंग
- विलंबित, किंवा आळशी, आरंभीकरण जर तुम्ही आवश्यकतेनुसार एखादी वस्तू लोड करू शकत असाल तर लगेच लोड का करावे?
- लॉगिंग विनंत्या
- डेटा आणि प्रवेशाचे मध्यवर्ती सत्यापन
- कामगार थ्रेड सुरू करत आहे
- ऑब्जेक्टमध्ये प्रवेश रेकॉर्ड करणे
फायदे आणि तोटे
- + तुम्ही तुमच्या इच्छेनुसार सेवा ऑब्जेक्टवर प्रवेश नियंत्रित करू शकता
- + सेवा ऑब्जेक्टचे जीवन चक्र व्यवस्थापित करण्याशी संबंधित अतिरिक्त क्षमता
- + हे सर्व्हिस ऑब्जेक्टशिवाय कार्य करते
- + हे कार्यप्रदर्शन आणि कोड सुरक्षा सुधारते.
- - अतिरिक्त विनंत्यांमुळे कार्यप्रदर्शन खराब होण्याची जोखीम आहे
- - हे वर्ग पदानुक्रम अधिक क्लिष्ट करते
सराव मध्ये प्रॉक्सी नमुना
हार्ड डिस्कवरून ट्रेनचे वेळापत्रक वाचणारी प्रणाली लागू करूया:
public interface TrainTimetable {
String[] getTimetable();
String getTrainDepartureTime();
}
येथे मुख्य इंटरफेस लागू करणारा वर्ग आहे:
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 "";
}
}
प्रत्येक वेळी जेव्हा तुम्हाला ट्रेनचे वेळापत्रक मिळते तेव्हा प्रोग्राम डिस्कवरून फाइल वाचतो. पण ही फक्त आपल्या त्रासाची सुरुवात आहे. प्रत्येक वेळी एका ट्रेनचे वेळापत्रक मिळाल्यावर संपूर्ण फाईल वाचली जाते! हे चांगले आहे की असा कोड फक्त काय करू नये याच्या उदाहरणांमध्ये अस्तित्वात आहे :) क्लायंट वर्ग:
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]);
}
}
}
उदाहरण फाइल:
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
चला याची चाचणी करूया:
public static void main(String[] args) {
TimetableDisplay timetableDisplay = new timetableDisplay();
timetableDisplay.printTimetable();
}
आउटपुट:
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
आता आपल्या पॅटर्नची ओळख करून देण्यासाठी आवश्यक असलेल्या पायऱ्यांमधून जाऊ या:
-
मूळ ऑब्जेक्ट ऐवजी प्रॉक्सी वापरण्यास अनुमती देणारा इंटरफेस परिभाषित करा. आमच्या उदाहरणात, हे आहे
TrainTimetable
. -
प्रॉक्सी वर्ग तयार करा. त्यात सर्व्हिस ऑब्जेक्टचा संदर्भ असावा (त्याला वर्गात तयार करा किंवा कन्स्ट्रक्टरकडे द्या).
आमचा प्रॉक्सी वर्ग येथे आहे:
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; } }
या टप्प्यावर, आम्ही मूळ ऑब्जेक्टच्या संदर्भासह एक क्लास तयार करत आहोत आणि सर्व कॉल्स त्यावर फॉरवर्ड करत आहोत.
-
प्रॉक्सी क्लासचे तर्क लागू करूया. मूलभूतपणे, कॉल नेहमी मूळ ऑब्जेक्टवर पुनर्निर्देशित केले जातात.
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; } }
getTimetable()
टाइमटेबल अॅरे मेमरीमध्ये कॅश केले गेले आहे की नाही ते तपासते . नसल्यास, ते डिस्कवरून डेटा लोड करण्याची विनंती पाठवते आणि परिणाम जतन करते. जर वेळापत्रकाची आधीच विनंती केली गेली असेल, तर ते त्वरीत मेमरीमधून ऑब्जेक्ट परत करते.त्याच्या साध्या कार्यक्षमतेबद्दल धन्यवाद, getTrainDepartureTime() पद्धतीला मूळ ऑब्जेक्टवर पुनर्निर्देशित करण्याची गरज नव्हती. आम्ही फक्त नवीन पद्धतीमध्ये त्याची कार्यक्षमता डुप्लिकेट केली आहे.
हे करू नका. जर तुम्हाला कोड डुप्लिकेट करायचा असेल किंवा तत्सम काहीतरी करायचे असेल, तर काहीतरी चूक झाली आहे आणि तुम्हाला समस्येकडे पुन्हा वेगळ्या कोनातून पाहण्याची आवश्यकता आहे. आमच्या साध्या उदाहरणात, आमच्याकडे दुसरा पर्याय नव्हता. परंतु वास्तविक प्रकल्पांमध्ये, कोड बहुधा अधिक योग्यरित्या लिहिला जाईल.
-
क्लायंट कोडमध्ये, मूळ ऑब्जेक्टऐवजी प्रॉक्सी ऑब्जेक्ट तयार करा:
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]); } } }
तपासा
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
छान, ते योग्यरित्या कार्य करते.
तुम्ही विशिष्ट अटींवर अवलंबून मूळ ऑब्जेक्ट आणि प्रॉक्सी ऑब्जेक्ट दोन्ही तयार करणाऱ्या कारखान्याच्या पर्यायाचा विचार करू शकता.
GO TO FULL VERSION