কেন আপনি একটি প্রক্সি প্রয়োজন?
এই প্যাটার্ন একটি বস্তুর নিয়ন্ত্রিত অ্যাক্সেসের সাথে যুক্ত সমস্যা সমাধান করতে সাহায্য করে। আপনি জিজ্ঞাসা করতে পারেন, "কেন আমাদের নিয়ন্ত্রিত অ্যাক্সেসের প্রয়োজন?" আসুন কয়েকটি পরিস্থিতি দেখি যা আপনাকে কী তা বুঝতে সাহায্য করবে।উদাহরণ 1
কল্পনা করুন যে আমাদের কাছে একগুচ্ছ পুরানো কোড সহ একটি বড় প্রকল্প রয়েছে, যেখানে একটি ডাটাবেস থেকে প্রতিবেদনগুলি রপ্তানির জন্য দায়ী একটি শ্রেণী রয়েছে। ক্লাস সিঙ্ক্রোনাসভাবে কাজ করে। অর্থাৎ, ডাটাবেস অনুরোধটি প্রক্রিয়া করার সময় পুরো সিস্টেমটি নিষ্ক্রিয় থাকে। গড়ে, একটি প্রতিবেদন তৈরি করতে 30 মিনিট সময় লাগে৷ তদনুসারে, রপ্তানি প্রক্রিয়া শুরু হয় 12:30 AM, এবং ব্যবস্থাপনা সকালে রিপোর্ট পায়। একটি অডিট প্রকাশ করেছে যে স্বাভাবিক ব্যবসায়িক সময়ের মধ্যে অবিলম্বে প্রতিবেদনটি গ্রহণ করতে সক্ষম হওয়া ভাল হবে৷ শুরুর সময় স্থগিত করা যাবে না, এবং ডাটাবেস থেকে প্রতিক্রিয়ার জন্য অপেক্ষা করার সময় সিস্টেমটি ব্লক করতে পারে না। সমাধান হল সিস্টেমটি কীভাবে কাজ করে তা পরিবর্তন করা, একটি পৃথক থ্রেডে প্রতিবেদন তৈরি এবং রপ্তানি করা। এই সমাধানটি সিস্টেমটিকে যথারীতি কাজ করতে দেবে এবং ব্যবস্থাপনা নতুন প্রতিবেদন পাবে। যাহোক, একটি সমস্যা আছে: বর্তমান কোডটি পুনরায় লেখা যাবে না, যেহেতু সিস্টেমের অন্যান্য অংশ এর কার্যকারিতা ব্যবহার করে। এই ক্ষেত্রে, আমরা একটি মধ্যবর্তী প্রক্সি ক্লাস প্রবর্তন করতে প্রক্সি প্যাটার্ন ব্যবহার করতে পারি যা রিপোর্ট রপ্তানি করার জন্য অনুরোধ পাবে, শুরুর সময় লগ করবে এবং একটি পৃথক থ্রেড চালু করবে। একবার রিপোর্ট তৈরি হলে, থ্রেডটি বন্ধ হয়ে যায় এবং সবাই খুশি হয়।উদাহরণ 2
একটি উন্নয়ন দল একটি ইভেন্ট ওয়েবসাইট তৈরি করছে। নতুন ইভেন্টের ডেটা পেতে, দলটি তৃতীয় পক্ষের পরিষেবার জন্য জিজ্ঞাসা করে। একটি বিশেষ ব্যক্তিগত লাইব্রেরি পরিষেবার সাথে মিথস্ক্রিয়া সহজতর করে৷ বিকাশের সময়, একটি সমস্যা আবিষ্কৃত হয়: তৃতীয় পক্ষের সিস্টেম দিনে একবার তার ডেটা আপডেট করে, কিন্তু প্রতিবার যখন ব্যবহারকারী একটি পৃষ্ঠা রিফ্রেশ করে তখন একটি অনুরোধ পাঠানো হয়। এটি প্রচুর সংখ্যক অনুরোধ তৈরি করে এবং পরিষেবাটি সাড়া দেওয়া বন্ধ করে দেয়। সমাধান হল পরিষেবার প্রতিক্রিয়া ক্যাশে করা এবং পৃষ্ঠাগুলি পুনরায় লোড হওয়ার সাথে সাথে ক্যাশে আপডেট করা দর্শকদের কাছে ক্যাশ করা ফলাফল ফিরিয়ে দেওয়া। এই ক্ষেত্রে, প্রক্সি ডিজাইন প্যাটার্ন একটি চমৎকার সমাধান যা বিদ্যমান কার্যকারিতা পরিবর্তন করে না।নকশা প্যাটার্ন পিছনে নীতি
এই প্যাটার্ন বাস্তবায়ন করতে, আপনাকে একটি প্রক্সি ক্লাস তৈরি করতে হবে। এটি পরিষেবা শ্রেণীর ইন্টারফেস প্রয়োগ করে, ক্লায়েন্ট কোডের জন্য এর আচরণ অনুকরণ করে। এই পদ্ধতিতে, ক্লায়েন্ট আসল বস্তুর পরিবর্তে একটি প্রক্সির সাথে যোগাযোগ করে। একটি নিয়ম হিসাবে, সমস্ত অনুরোধ পরিষেবা ক্লাসে প্রেরণ করা হয়, তবে আগে বা পরে অতিরিক্ত ক্রিয়া সহ। সহজভাবে বলতে গেলে, একটি প্রক্সি হল ক্লায়েন্ট কোড এবং লক্ষ্য বস্তুর মধ্যে একটি স্তর। একটি পুরানো এবং খুব ধীর হার্ড ডিস্ক থেকে ক্যাশিং ক্যোয়ারী ফলাফলের উদাহরণ বিবেচনা করুন। ধরুন আমরা কিছু প্রাচীন অ্যাপে বৈদ্যুতিক ট্রেনের সময়সূচীর কথা বলছি যার যুক্তি পরিবর্তন করা যায় না। একটি আপডেট সময়সূচী সহ একটি ডিস্ক একটি নির্দিষ্ট সময়ে প্রতিদিন ঢোকানো হয়। তাহলে আমাদের আছে: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