CodeGym /جاوا بلاگ /Random-UR /پراکسی ڈیزائن پیٹرن
John Squirrels
سطح
San Francisco

پراکسی ڈیزائن پیٹرن

گروپ میں شائع ہوا۔
پروگرامنگ میں، یہ ضروری ہے کہ آپ اپنی ایپلیکیشن کے فن تعمیر کی صحیح منصوبہ بندی کریں۔ ڈیزائن پیٹرن اس کو پورا کرنے کا ایک ناگزیر طریقہ ہے۔ آج پراکسی کے بارے میں بات کرتے ہیں۔

آپ کو پراکسی کی ضرورت کیوں ہے؟

یہ نمونہ کسی شے تک کنٹرول شدہ رسائی سے وابستہ مسائل کو حل کرنے میں مدد کرتا ہے۔ آپ پوچھ سکتے ہیں، "ہمیں کنٹرول شدہ رسائی کی ضرورت کیوں ہے؟" آئیے کچھ ایسے حالات دیکھیں جو آپ کو یہ جاننے میں مدد کریں گے کہ کیا ہے۔

مثال 1

تصور کریں کہ ہمارے پاس پرانے کوڈ کے ایک گروپ کے ساتھ ایک بڑا پروجیکٹ ہے، جہاں ڈیٹا بیس سے رپورٹس برآمد کرنے کے لیے ایک کلاس ذمہ دار ہے۔ کلاس ہم آہنگی سے کام کرتی ہے۔ یعنی، پورا نظام بیکار ہے جبکہ ڈیٹا بیس درخواست پر کارروائی کرتا ہے۔ اوسطاً، رپورٹ بنانے میں 30 منٹ لگتے ہیں۔ اس کے مطابق، برآمدی عمل 12:30 بجے شروع ہوتا ہے، اور انتظامیہ کو صبح رپورٹ موصول ہوتی ہے۔ ایک آڈٹ نے انکشاف کیا کہ عام کاروباری اوقات کے دوران فوری طور پر رپورٹ موصول کرنا بہتر ہوگا۔ آغاز کا وقت ملتوی نہیں کیا جا سکتا، اور سسٹم بلاک نہیں کر سکتا جب تک کہ یہ ڈیٹا بیس سے جواب کا انتظار کر رہا ہو۔ اس کا حل یہ ہے کہ سسٹم کے کام کرنے کے طریقے کو تبدیل کیا جائے، رپورٹ کو ایک علیحدہ تھریڈ پر بنانا اور برآمد کرنا۔ یہ حل نظام کو معمول کے مطابق کام کرنے دے گا، اور انتظامیہ کو تازہ رپورٹیں موصول ہوں گی۔ تاہم، ایک مسئلہ ہے: موجودہ کوڈ کو دوبارہ نہیں لکھا جا سکتا، کیونکہ سسٹم کے دوسرے حصے اس کی فعالیت کو استعمال کرتے ہیں۔ اس صورت میں، ہم ایک انٹرمیڈیٹ پراکسی کلاس متعارف کروانے کے لیے پراکسی پیٹرن کا استعمال کر سکتے ہیں جو رپورٹس برآمد کرنے، آغاز کے وقت کو لاگ ان کرنے، اور ایک الگ تھریڈ شروع کرنے کی درخواستیں وصول کرے گی۔ رپورٹ تیار ہونے کے بعد تھریڈ ختم ہو جاتا ہے اور سب خوش ہوتے ہیں۔

مثال 2

ایک ترقیاتی ٹیم ایونٹس کی ویب سائٹ بنا رہی ہے۔ نئے واقعات کا ڈیٹا حاصل کرنے کے لیے، ٹیم تیسرے فریق کی خدمت سے استفسار کرتی ہے۔ ایک خصوصی نجی لائبریری سروس کے ساتھ تعامل کی سہولت فراہم کرتی ہے۔ ترقی کے دوران، ایک مسئلہ دریافت ہوتا ہے: فریق ثالث کا نظام دن میں ایک بار اپنے ڈیٹا کو اپ ڈیٹ کرتا ہے، لیکن جب بھی صارف کسی صفحہ کو ریفریش کرتا ہے تو اسے ایک درخواست بھیجی جاتی ہے۔ اس سے درخواستوں کی ایک بڑی تعداد پیدا ہوتی ہے، اور سروس جواب دینا بند کر دیتی ہے۔ اس کا حل یہ ہے کہ سروس کے جواب کو کیش کیا جائے اور کیش شدہ نتیجہ زائرین کو واپس کر دیا جائے جیسے ہی صفحات دوبارہ لوڈ ہو جائیں، ضرورت کے مطابق کیش کو اپ ڈیٹ کریں۔ اس صورت میں، پراکسی ڈیزائن پیٹرن ایک بہترین حل ہے جو موجودہ فعالیت کو تبدیل نہیں کرتا ہے۔

ڈیزائن پیٹرن کے پیچھے اصول

اس پیٹرن کو نافذ کرنے کے لیے، آپ کو ایک پراکسی کلاس بنانے کی ضرورت ہے۔ یہ سروس کلاس کے انٹرفیس کو لاگو کرتا ہے، کلائنٹ کوڈ کے لیے اس کے رویے کی نقل کرتا ہے۔ اس طریقے سے، کلائنٹ اصلی چیز کے بجائے پراکسی کے ساتھ بات چیت کرتا ہے۔ ایک اصول کے طور پر، تمام درخواستیں سروس کلاس کو منتقل کی جاتی ہیں، لیکن اس سے پہلے یا بعد میں اضافی کارروائیوں کے ساتھ۔ سیدھے الفاظ میں، ایک پراکسی کلائنٹ کوڈ اور ٹارگٹ آبجیکٹ کے درمیان ایک پرت ہے۔ ایک پرانی اور بہت سست ہارڈ ڈسک سے کیشنگ استفسار کے نتائج کی مثال پر غور کریں۔ فرض کریں کہ ہم کسی قدیم ایپ میں الیکٹرک ٹرینوں کے ٹائم ٹیبل کے بارے میں بات کر رہے ہیں جس کی منطق کو تبدیل نہیں کیا جا سکتا۔ ایک اپ ڈیٹ ٹائم ٹیبل کے ساتھ ایک ڈسک ہر روز ایک مقررہ وقت پر ڈالی جاتی ہے۔ تو، ہمارے پاس ہے:
  1. TrainTimetableانٹرفیس
  2. ElectricTrainTimetable، جو اس انٹرفیس کو نافذ کرتا ہے۔
  3. کلائنٹ کوڈ اس کلاس کے ذریعے فائل سسٹم کے ساتھ تعامل کرتا ہے۔
  4. TimetableDisplayکلائنٹ کلاس. اس کا printTimetable()طریقہ کلاس کے طریقے استعمال کرتا ہے ElectricTrainTimetable۔
خاکہ آسان ہے: پراکسی ڈیزائن پیٹرن: - 2فی الحال، printTimetable()طریقہ کار کی ہر کال کے ساتھ، ElectricTrainTimetableکلاس ڈسک تک رسائی حاصل کرتی ہے، ڈیٹا لوڈ کرتی ہے، اور اسے کلائنٹ کو پیش کرتی ہے۔ سسٹم ٹھیک کام کرتا ہے، لیکن یہ بہت سست ہے۔ نتیجے کے طور پر، کیشنگ میکانزم کو شامل کرکے سسٹم کی کارکردگی کو بڑھانے کا فیصلہ کیا گیا۔ یہ پراکسی پیٹرن کا استعمال کرتے ہوئے کیا جا سکتا ہے: پراکسی ڈیزائن پیٹرن: - 3اس طرح، TimetableDisplayکلاس کو یہ بھی محسوس نہیں ہوتا ہے کہ وہ ElectricTrainTimetableProxyپرانی کلاس کے بجائے کلاس کے ساتھ بات چیت کر رہی ہے۔ نیا نفاذ دن میں ایک بار ٹائم ٹیبل لوڈ کرتا ہے۔ دوبارہ درخواستوں کے لیے، یہ میموری سے پہلے سے بھری ہوئی چیز کو واپس کرتا ہے۔

پراکسی کے لیے کون سے کام بہترین ہیں؟

یہاں کچھ حالات ہیں جہاں یہ نمونہ یقینی طور پر کام آئے گا:
  1. کیشنگ
  2. تاخیر، یا سست، ابتداء اگر آپ اسے ضرورت کے مطابق لوڈ کر سکتے ہیں تو فوری طور پر کیوں لوڈ کریں؟
  3. لاگنگ کی درخواستیں۔
  4. ڈیٹا اور رسائی کی درمیانی تصدیق
  5. ورکر تھریڈز شروع کر رہے ہیں۔
  6. کسی شے تک رسائی کی ریکارڈنگ
اور استعمال کے دیگر معاملات بھی ہیں۔ اس پیٹرن کے پیچھے اصول کو سمجھ کر، آپ ان حالات کی نشاندہی کر سکتے ہیں جہاں اسے کامیابی سے لاگو کیا جا سکتا ہے۔ پہلی نظر میں، ایک پراکسی وہی کام کرتی ہے جیسا کہ اگواڑا ، لیکن ایسا نہیں ہے۔ ایک پراکسی کا انٹرفیس وہی ہوتا ہے جیسا کہ سروس آبجیکٹ۔ اس کے علاوہ، اس پیٹرن کو ڈیکوریٹر یا اڈاپٹر پیٹرن کے ساتھ الجھائیں نہیں ۔ ڈیکوریٹر ایک توسیعی انٹرفیس فراہم کرتا ہے، اور ایک اڈاپٹر متبادل انٹرفیس فراہم کرتا ہے ۔

فائدے اور نقصانات

  • + آپ جس طرح چاہیں سروس آبجیکٹ تک رسائی کو کنٹرول کرسکتے ہیں۔
  • + سروس آبجیکٹ کے لائف سائیکل کو منظم کرنے سے متعلق اضافی صلاحیتیں۔
  • + یہ سروس آبجیکٹ کے بغیر کام کرتا ہے۔
  • + یہ کارکردگی اور کوڈ سیکیورٹی کو بہتر بناتا ہے۔
  • - اضافی درخواستوں کی وجہ سے کارکردگی خراب ہونے کا خطرہ ہے۔
  • - یہ طبقاتی درجہ بندی کو مزید پیچیدہ بناتا ہے۔

عملی طور پر پراکسی پیٹرن

آئیے ایک ایسا نظام نافذ کریں جو ہارڈ ڈسک سے ٹرین کے ٹائم ٹیبل پڑھتا ہے:
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
اب آئیے اپنے پیٹرن کو متعارف کرانے کے لیے درکار اقدامات پر چلتے ہیں:
  1. ایک انٹرفیس کی وضاحت کریں جو اصل آبجیکٹ کے بجائے پراکسی کے استعمال کی اجازت دیتا ہے۔ ہماری مثال میں، یہ ہے TrainTimetable.

  2. پراکسی کلاس بنائیں۔ اس میں سروس آبجیکٹ کا حوالہ ہونا چاہئے (اسے کلاس میں بنائیں یا کنسٹرکٹر کو پاس کریں)۔

    یہاں ہماری پراکسی کلاس ہے:

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

    اس مرحلے پر، ہم صرف اصل آبجیکٹ کے حوالے سے ایک کلاس بنا رہے ہیں اور تمام کالز کو اس پر بھیج رہے ہیں۔

  3. آئیے پراکسی کلاس کی منطق کو لاگو کرتے ہیں۔ بنیادی طور پر، کالوں کو ہمیشہ اصل آبجیکٹ پر ری ڈائریکٹ کیا جاتا ہے۔

    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() طریقہ کو اصل آبجیکٹ کی طرف ری ڈائریکٹ کرنے کی ضرورت نہیں تھی۔ ہم نے آسانی سے اس کی فعالیت کو ایک نئے طریقہ میں نقل کیا۔

    ایسا مت کرو۔ اگر آپ کو کوڈ کو ڈپلیکیٹ کرنا ہے یا کچھ ایسا ہی کرنا ہے، تو کچھ غلط ہو گیا، اور آپ کو مسئلے کو ایک مختلف زاویے سے دوبارہ دیکھنے کی ضرورت ہے۔ ہماری سادہ مثال میں، ہمارے پاس کوئی دوسرا آپشن نہیں تھا۔ لیکن حقیقی منصوبوں میں، کوڈ زیادہ تر ممکنہ طور پر زیادہ درست لکھا جائے گا۔

  4. کلائنٹ کوڈ میں، اصل آبجیکٹ کے بجائے ایک پراکسی آبجیکٹ بنائیں:

    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

    بہت اچھا، یہ صحیح طریقے سے کام کرتا ہے۔

    آپ کسی ایسی فیکٹری کے آپشن پر بھی غور کر سکتے ہیں جو کچھ شرائط پر منحصر ہو کر ایک اصل آبجیکٹ اور پراکسی آبجیکٹ دونوں بنائے۔

الوداع کہنے سے پہلے، یہاں ایک مددگار لنک ہے۔

آج کیلئے بس اتنا ہی! اسباق پر واپس جانا اور اپنے نئے علم کو عملی طور پر آزمانا برا خیال نہیں ہوگا :)
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION