JavaRush /جاوا بلاگ /Random-UR /پراکسی ڈیزائن پیٹرن

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

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

آپ کو نائب کی ضرورت کیوں ہے؟

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

مثال 1

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

مثال 2

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

پیٹرن کیسے کام کرتا ہے۔

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

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

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

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

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

عملی طور پر متبادل پیٹرن

آئیے آپ کے ساتھ ایک ایسا نظام نافذ کرتے ہیں جو ڈسک سے ٹرین کے نظام الاوقات کو پڑھتا ہے:
public interface TimetableTrains {
   String[] getTimetable();
   String getTrainDepartureTime();
}
ایک کلاس جو مرکزی انٹرفیس کو نافذ کرتی ہے:
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 "";
   }
}
جب بھی آپ تمام ٹرینوں کا شیڈول حاصل کرنے کی کوشش کرتے ہیں، پروگرام ڈسک سے فائل پڑھتا ہے۔ لیکن یہ پھر بھی پھول ہیں۔ فائل کو ہر بار پڑھا جاتا ہے جب آپ کو صرف ایک ٹرین کا شیڈول حاصل کرنے کی ضرورت ہوتی ہے! یہ اچھا ہے کہ ایسا کوڈ صرف بری مثالوں میں موجود ہے :) کلائنٹ کلاس:
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]);
       }
   }
}
مثال کی فائل:

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
آئیے ٹیسٹ کریں:
public static void main(String[] args) {
   DisplayTimetable displayTimetable = new DisplayTimetable();
   displayTimetable.printTimetable();
}
نتیجہ:

Поезд  Откуда  Куда   Время отправления Время прибытия    Время в пути
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
اب آئیے اپنے پیٹرن کو نافذ کرنے کے مراحل سے گزرتے ہیں:
  1. ایک انٹرفیس کی وضاحت کریں جو آپ کو اصل آبجیکٹ کے بجائے ایک نئی پراکسی استعمال کرنے کی اجازت دیتا ہے۔ ہماری مثال میں یہ ہے TimetableTrains۔

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

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

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

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

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

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

    طریقہ getTimetable()چیک کرتا ہے کہ آیا شیڈول سرنی میموری میں محفوظ ہے۔ اگر نہیں، تو یہ ڈسک سے ڈیٹا لوڈ کرنے کی درخواست جاری کرتا ہے، نتیجہ کو اسٹور کرتا ہے۔ اگر درخواست پہلے سے چل رہی ہے، تو یہ جلدی سے میموری سے کسی چیز کو واپس کر دے گی۔

    اس کی سادہ فعالیت کی بدولت getTrainDepartireTime() طریقہ کو اصل آبجیکٹ کی طرف ری ڈائریکٹ کرنے کی ضرورت نہیں تھی۔ ہم نے آسانی سے اس کی فعالیت کو ایک نئے طریقہ میں نقل کیا۔

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

  4. کلائنٹ کوڈ میں اصل آبجیکٹ کی تخلیق کو متبادل آبجیکٹ سے تبدیل کریں:

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

    امتحان

    
    Поезд  Откуда  Куда   Время отправления Время прибытия    Время в пути
    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

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

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

ڈاٹ کی بجائے مفید لنک

  1. پیٹرن کے بارے میں بہترین مضمون اور "نائب" کے بارے میں تھوڑا سا

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