JavaRush /جاوا بلاگ /Random-UR /جاوا میں ڈیزائن پیٹرن
Viacheslav
سطح

جاوا میں ڈیزائن پیٹرن

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

ٹیمپلیٹس

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

    یہ نمونے آبجیکٹ کی تخلیق کو لچکدار بنانے کا مسئلہ حل کرتے ہیں۔

  • ساختی

    یہ پیٹرن اشیاء کے درمیان مؤثر طریقے سے کنکشن بنانے کے مسئلے کو حل کرتے ہیں

  • برتاؤ

    یہ نمونے اشیاء کے درمیان موثر تعامل کا مسئلہ حل کرتے ہیں۔

مثالوں پر غور کرنے کے لیے، میں آن لائن کوڈ کمپائلر repl.it استعمال کرنے کا مشورہ دیتا ہوں۔
جاوا میں ڈیزائن پیٹرن - 2

تخلیقی نمونے۔

آئیے اشیاء کی زندگی کے دور کے آغاز سے شروع کرتے ہیں - اشیاء کی تخلیق کے ساتھ۔ جنریٹیو ٹیمپلیٹس اشیاء کو زیادہ آسانی سے بنانے میں مدد کرتے ہیں اور اس عمل میں لچک فراہم کرتے ہیں۔ سب سے مشہور میں سے ایک " بلڈر " ہے۔ یہ پیٹرن آپ کو مرحلہ وار پیچیدہ اشیاء بنانے کی اجازت دیتا ہے۔ جاوا میں، سب سے مشہور مثال یہ ہے StringBuilder:
class Main {
  public static void main(String[] args) {
    StringBuilder builder = new StringBuilder();
    builder.append("Hello");
    builder.append(',');
    builder.append("World!");
    System.out.println(builder.toString());
  }
}
کسی چیز کو بنانے کا ایک اور معروف طریقہ تخلیق کو ایک الگ طریقہ میں منتقل کرنا ہے۔ یہ طریقہ ایک آبجیکٹ فیکٹری بن جاتا ہے۔ اسی لیے پیٹرن کو " فیکٹری میتھڈ" کہا جاتا ہے۔ جاوا میں، مثال کے طور پر، اس کا اثر کلاس میں دیکھا جا سکتا ہے java.util.Calendar۔ کلاس خود Calendarخلاصہ ہے، اور اسے بنانے کے لیے طریقہ استعمال کیا جاتا ہے getInstance:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.getTime());
    System.out.println(calendar.getClass().getCanonicalName());
  }
}
ایسا اکثر ہوتا ہے کیونکہ آبجیکٹ کی تخلیق کے پیچھے منطق پیچیدہ ہو سکتی ہے۔ مثال کے طور پر، اوپر کی صورت میں، ہم بیس کلاس تک رسائی حاصل کرتے ہیں Calendar، اور ایک کلاس بن جاتی ہے GregorianCalendar۔ اگر ہم کنسٹرکٹر کو دیکھیں تو ہم دیکھ سکتے ہیں کہ حالات کے لحاظ سے مختلف نفاذات بنائے گئے ہیں Calendar۔ لیکن بعض اوقات ایک فیکٹری کا طریقہ کافی نہیں ہوتا ہے۔ بعض اوقات آپ کو مختلف اشیاء بنانے کی ضرورت ہوتی ہے تاکہ وہ ایک ساتھ فٹ ہوں۔ ایک اور ٹیمپلیٹ اس میں ہماری مدد کرے گا - " خلاصہ کارخانہ "۔ اور پھر ہمیں ایک جگہ مختلف فیکٹریاں بنانے کی ضرورت ہے۔ ایک ہی وقت میں، فائدہ یہ ہے کہ نفاذ کی تفصیلات ہمارے لیے اہم نہیں ہیں، یعنی اس سے کوئی فرق نہیں پڑتا کہ ہمیں کون سی مخصوص فیکٹری ملتی ہے۔ اہم بات یہ ہے کہ یہ صحیح عمل درآمد کرتا ہے۔ سپر مثال:
جاوا میں ڈیزائن پیٹرن - 3
یعنی ماحول (آپریٹنگ سسٹم) پر منحصر ہے، ہمیں ایک خاص فیکٹری ملے گی جو ہم آہنگ عناصر بنائے گی۔ کسی اور کے ذریعے تخلیق کرنے کے متبادل کے طور پر، ہم " پروٹو ٹائپ " پیٹرن استعمال کر سکتے ہیں۔ اس کا جوہر سادہ ہے - نئی اشیاء پہلے سے موجود اشیاء کی شبیہ اور مشابہت میں تخلیق کی جاتی ہیں، یعنی ان کے پروٹوٹائپ کے مطابق. جاوا میں، ہر کوئی اس پیٹرن کو دیکھتا ہے - یہ ایک انٹرفیس کا استعمال ہے java.lang.Cloneable:
class Main {
  public static void main(String[] args) {
    class CloneObject implements Cloneable {
      @Override
      protected Object clone() throws CloneNotSupportedException {
        return new CloneObject();
      }
    }
    CloneObject obj = new CloneObject();
    try {
      CloneObject pattern = (CloneObject) obj.clone();
    } catch (CloneNotSupportedException e) {
      //Do something
    }
  }
}
جیسا کہ آپ دیکھ سکتے ہیں، فون کرنے والا نہیں جانتا کہ کیسے clone۔ یعنی پروٹو ٹائپ پر مبنی آبجیکٹ بنانا خود آبجیکٹ کی ذمہ داری ہے۔ یہ مفید ہے کیونکہ یہ صارف کو ٹیمپلیٹ آبجیکٹ کے نفاذ سے منسلک نہیں کرتا ہے۔ ٹھیک ہے، اس فہرست میں سب سے آخری "سنگلٹن" پیٹرن ہے۔ اس کا مقصد آسان ہے - پوری ایپلیکیشن کے لیے آبجیکٹ کی ایک مثال فراہم کرنا۔ یہ پیٹرن دلچسپ ہے کیونکہ یہ اکثر ملٹی تھریڈنگ کے مسائل کو ظاہر کرتا ہے۔ مزید گہرائی سے دیکھنے کے لیے، ان مضامین کو دیکھیں:
جاوا میں ڈیزائن پیٹرن - 4

ساختی نمونے۔

اشیاء کی تخلیق کے ساتھ یہ واضح ہوتا گیا۔ اب وقت آگیا ہے کہ ساختی نمونوں کو دیکھیں۔ ان کا مقصد آسان مدد کرنے والے طبقاتی درجہ بندی اور ان کے تعلقات بنانا ہے۔ سب سے پہلے اور معروف نمونوں میں سے ایک " ڈپٹی " (پراکسی) ہے۔ پراکسی کا انٹرفیس وہی ہے جو اصلی چیز کا ہے، اس لیے کلائنٹ کے لیے پراکسی کے ذریعے یا براہ راست کام کرنے سے کوئی فرق نہیں پڑتا۔ سب سے آسان مثال java.lang.reflect.Proxy ہے :
import java.util.*;
import java.lang.reflect.*;
class Main {
  public static void main(String[] arguments) {
    final Map<String, String> original = new HashMap<>();
    InvocationHandler proxy = (obj, method, args) -> {
      System.out.println("Invoked: " + method.getName());
      return method.invoke(original, args);
    };
    Map<String, String> proxyInstance = (Map) Proxy.newProxyInstance(
        original.getClass().getClassLoader(),
        original.getClass().getInterfaces(),
        proxy);
    proxyInstance.put("key", "value");
    System.out.println(proxyInstance.get("key"));
  }
}
جیسا کہ آپ دیکھ سکتے ہیں، مثال میں ہمارے پاس اصل ہے - یہ وہی ہے HashMapجو انٹرفیس کو نافذ کرتا ہے Map۔ HashMapاس کے بعد ہم ایک پراکسی بناتے ہیں جو کلائنٹ کے حصے کے لیے اصل کی جگہ لے لیتا ہے ، جو کال کے دوران ہماری اپنی منطق کا اضافہ کرتے ہوئے putاور طریقوں کو کال کرتا ہے۔ getجیسا کہ ہم دیکھ سکتے ہیں، پیٹرن میں تعامل انٹرفیس کے ذریعے ہوتا ہے۔ لیکن بعض اوقات ایک متبادل کافی نہیں ہوتا ہے۔ اور پھر " ڈیکوریٹر " پیٹرن استعمال کیا جا سکتا ہے۔ ڈیکوریٹر کو ریپر یا ریپر بھی کہا جاتا ہے۔ پراکسی اور ڈیکوریٹر بہت ملتے جلتے ہیں، لیکن اگر آپ مثال دیکھیں تو آپ کو فرق نظر آئے گا:
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    List<String> list = new ArrayList<>();
    List<String> decorated = Collections.checkedList(list, String.class);
    decorated.add("2");
    list.add("3");
    System.out.println(decorated);
  }
}
پراکسی کے برعکس، ایک ڈیکوریٹر اپنے آپ کو کسی ایسی چیز کے گرد لپیٹتا ہے جسے ان پٹ کے طور پر پاس کیا جاتا ہے۔ ایک پراکسی دونوں کو قبول کر سکتا ہے جسے پراکسی کرنے کی ضرورت ہے اور پراکسی آبجیکٹ کی زندگی کا انتظام بھی کر سکتی ہے (مثال کے طور پر، ایک پراکسی آبجیکٹ بنائیں)۔ ایک اور دلچسپ نمونہ ہے - " اڈاپٹر "۔ یہ ڈیکوریٹر کی طرح ہے - ڈیکوریٹر ایک شے کو بطور ان پٹ لیتا ہے اور اس شے پر ایک ریپر واپس کرتا ہے۔ فرق یہ ہے کہ مقصد فعالیت کو تبدیل کرنا نہیں ہے، بلکہ ایک انٹرفیس کو دوسرے کے ساتھ ڈھالنا ہے۔ جاوا کے پاس اس کی بہت واضح مثال ہے:
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    String[] array = {"One", "Two", "Three"};
    List<String> strings = Arrays.asList(array);
    strings.set(0, "1");
    System.out.println(Arrays.toString(array));
  }
}
ان پٹ پر ہمارے پاس ایک صف ہے۔ اگلا، ہم ایک اڈاپٹر بناتے ہیں جو صف کو انٹرفیس پر لاتا ہے List۔ اس کے ساتھ کام کرتے وقت، ہم دراصل ایک صف کے ساتھ کام کر رہے ہیں۔ لہذا، عناصر کو شامل کرنا کام نہیں کرے گا، کیونکہ ... اصل صف کو تبدیل نہیں کیا جا سکتا۔ اور اس صورت میں ہم حاصل کریں گے UnsupportedOperationException۔ طبقاتی ڈھانچے کو تیار کرنے کے لیے اگلا دلچسپ نقطہ نظر جامع پیٹرن ہے ۔ یہ دلچسپ ہے کہ ایک انٹرفیس کا استعمال کرتے ہوئے عناصر کا ایک مخصوص سیٹ ایک مخصوص درخت کی طرح درجہ بندی میں ترتیب دیا جاتا ہے. والدین کے عنصر پر ایک طریقہ کو کال کرنے سے، ہمیں تمام ضروری چائلڈ عناصر پر اس طریقہ پر کال ملتی ہے۔ اس پیٹرن کی ایک اہم مثال UI ہے (وہ java.awt ہو یا JSF):
import java.awt.*;
class Main {
  public static void main(String[] arguments) {
    Container container = new Container();
    Component component = new java.awt.Component(){};
    System.out.println(component.getComponentOrientation().isLeftToRight());
    container.add(component);
    container.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
    System.out.println(component.getComponentOrientation().isLeftToRight());
  }
}
جیسا کہ ہم دیکھ سکتے ہیں، ہم نے کنٹینر میں ایک جزو شامل کیا ہے۔ اور پھر ہم نے کنٹینر سے اجزاء کی نئی سمت بندی کو لاگو کرنے کو کہا۔ اور کنٹینر، یہ جانتے ہوئے کہ یہ کن اجزاء پر مشتمل ہے، تمام بچوں کے اجزاء کو اس کمانڈ پر عمل درآمد سونپ دیا۔ ایک اور دلچسپ نمونہ " پل " پیٹرن ہے۔ اسے اس لیے کہا جاتا ہے کیونکہ یہ دو مختلف طبقاتی درجہ بندیوں کے درمیان تعلق یا پل کو بیان کرتا ہے۔ ان میں سے ایک درجہ بندی کو تجرید اور دوسرے کو نفاذ سمجھا جاتا ہے۔ اس پر روشنی ڈالی گئی ہے کیونکہ تجرید خود کارروائیاں نہیں کرتا ہے، بلکہ اس عمل کو عمل میں لاتا ہے۔ یہ پیٹرن اکثر اس وقت استعمال ہوتا ہے جب "کنٹرول" کلاسز اور "پلیٹ فارم" کلاسز کی کئی قسمیں ہوں (مثال کے طور پر، ونڈوز، لینکس، وغیرہ)۔ اس نقطہ نظر کے ساتھ، ان میں سے ایک درجہ بندی (تجزیہ) کو دوسرے درجہ بندی (عمل درآمد) کی اشیاء کا حوالہ ملے گا اور وہ بنیادی کام ان کے سپرد کرے گا۔ چونکہ تمام نفاذات ایک مشترکہ انٹرفیس کی پیروی کریں گے، ان کو تجرید کے اندر تبدیل کیا جا سکتا ہے۔ جاوا میں، اس کی ایک واضح مثال یہ ہے java.awt:
جاوا میں ڈیزائن پیٹرن - 5
مزید معلومات کے لیے، مضمون " جاوا AWT میں پیٹرنز " دیکھیں۔ ساختی نمونوں میں، میں " Facade " پیٹرن کو بھی نوٹ کرنا چاہوں گا۔ اس کا جوہر ایک آسان اور جامع انٹرفیس کے پیچھے اس API کے پیچھے لائبریریوں/فریم ورک کے استعمال کی پیچیدگی کو چھپانا ہے۔ مثال کے طور پر، آپ مثال کے طور پر JPA سے JSF یا EntityManager استعمال کر سکتے ہیں۔ ایک اور نمونہ بھی ہے جسے " فلائی ویٹ " کہا جاتا ہے۔ اس کا خلاصہ یہ ہے کہ اگر مختلف اشیاء کی ایک ہی حالت ہو تو اسے عام کیا جا سکتا ہے اور ہر چیز میں نہیں بلکہ ایک جگہ پر محفوظ کیا جا سکتا ہے۔ اور پھر ہر چیز ایک مشترکہ حصے کا حوالہ دے سکے گی، جس سے اسٹوریج کے لیے میموری کی لاگت کم ہو جائے گی۔ اس پیٹرن میں اکثر اشیاء کے تالاب کو پری کیشنگ یا برقرار رکھنا شامل ہوتا ہے۔ دلچسپ بات یہ ہے کہ ہم اس پیٹرن کو بھی شروع سے جانتے ہیں:
جاوا میں ڈیزائن پیٹرن - 6
اسی تشبیہ سے یہاں تاروں کا ایک تالاب شامل کیا جا سکتا ہے۔ آپ اس موضوع پر مضمون پڑھ سکتے ہیں: " فلائی ویٹ ڈیزائن پیٹرن
جاوا میں ڈیزائن پیٹرن - 7

طرز عمل کے نمونے۔

لہذا، ہم نے سوچا کہ اشیاء کیسے بنائی جا سکتی ہیں اور کلاسوں کے درمیان روابط کیسے منظم کیے جا سکتے ہیں۔ باقی سب سے دلچسپ چیز اشیاء کے رویے کو تبدیل کرنے میں لچک فراہم کرنا ہے۔ اور رویے کے نمونے اس میں ہماری مدد کریں گے۔ سب سے زیادہ ذکر کردہ پیٹرن میں سے ایک " حکمت عملی " پیٹرن ہے۔ یہیں سے کتاب " Head First. Design Patterns " میں نمونوں کا مطالعہ شروع ہوتا ہے۔ "حکمت عملی" پیٹرن کا استعمال کرتے ہوئے، ہم کسی چیز کے اندر ذخیرہ کر سکتے ہیں کہ ہم عمل کیسے کریں گے، یعنی اندر موجود چیز ایک حکمت عملی کو ذخیرہ کرتی ہے جسے کوڈ پر عمل درآمد کے دوران تبدیل کیا جا سکتا ہے۔ یہ ایک نمونہ ہے جو ہم اکثر موازنہ کرنے والے کا استعمال کرتے وقت استعمال کرتے ہیں:
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Comparator<String> comparator = Comparator.comparingInt(String::length);
    Set dataSet = new TreeSet(comparator);
    dataSet.addAll(data);
    System.out.println("Dataset : " + dataSet);
  }
}
ہمارے سامنے - TreeSet. TreeSetاس میں عناصر کی ترتیب کو برقرار رکھنے کا طرز عمل ہے ، یعنی ان کو ترتیب دیتا ہے (چونکہ یہ ایک ترتیب شدہ سیٹ ہے)۔ اس طرز عمل کی ایک طے شدہ حکمت عملی ہے، جسے ہم JavaDoc میں دیکھتے ہیں: "قدرتی ترتیب" میں چھانٹنا (سٹرنگز کے لیے، یہ لغت کی ترتیب ہے)۔ ایسا ہوتا ہے اگر آپ پیرامیٹر لیس کنسٹرکٹر استعمال کرتے ہیں۔ لیکن اگر ہم حکمت عملی کو تبدیل کرنا چاہتے ہیں، تو ہم گزر سکتے ہیں Comparator۔ اس مثال میں، ہم اپنے سیٹ کو بطور تشکیل دے سکتے ہیں new TreeSet(comparator)، اور پھر عناصر کو ذخیرہ کرنے کی ترتیب (اسٹوریج کی حکمت عملی) کمپیریٹر میں بیان کردہ ترتیب میں تبدیل ہو جائے گی۔ دلچسپ بات یہ ہے کہ تقریباً ایک ہی پیٹرن ہے جسے " State " کہا جاتا ہے۔ "ریاست" پیٹرن کہتا ہے کہ اگر ہمارے پاس مرکزی شے میں کچھ ایسا برتاؤ ہے جو اس شے کی حالت پر منحصر ہے، تو ہم خود ریاست کو ایک شے کے طور پر بیان کر سکتے ہیں اور ریاستی شے کو تبدیل کر سکتے ہیں۔ اور اصل اعتراض سے ریاست کو ڈیلیگیٹ کال کرتا ہے۔ ایک اور نمونہ جو ہمیں جاوا زبان کی بنیادی باتوں کا مطالعہ کرنے سے جانا جاتا ہے وہ ہے " کمانڈ " پیٹرن۔ یہ ڈیزائن پیٹرن بتاتا ہے کہ مختلف کمانڈز کو مختلف کلاسز کے طور پر پیش کیا جا سکتا ہے۔ یہ پیٹرن اسٹریٹجی پیٹرن سے بہت ملتا جلتا ہے۔ لیکن حکمت عملی کے پیٹرن میں، ہم اس کی وضاحت کر رہے تھے کہ ایک مخصوص عمل کیسے انجام دیا جائے گا (مثال کے طور پر، میں ترتیب دینا TreeSet)۔ "کمانڈ" پیٹرن میں، ہم دوبارہ وضاحت کرتے ہیں کہ کون سا عمل انجام دیا جائے گا۔ پیٹرن کمانڈ ہر روز ہمارے ساتھ ہوتی ہے جب ہم تھریڈز استعمال کرتے ہیں:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Runnable command = () -> {
      System.out.println("Command action");
    };
    Thread th = new Thread(command);
    th.start();
  }
}
جیسا کہ آپ دیکھ سکتے ہیں، کمانڈ ایک عمل یا کمانڈ کی وضاحت کرتا ہے جو ایک نئے دھاگے میں عمل میں لایا جائے گا۔ یہ " ذمہ داری کا سلسلہ " پیٹرن پر بھی غور کرنے کے قابل ہے۔ یہ پیٹرن بھی بہت آسان ہے۔ یہ پیٹرن کہتا ہے کہ اگر کسی چیز پر کارروائی کرنے کی ضرورت ہے، تو آپ ہینڈلرز کو ایک سلسلہ میں جمع کر سکتے ہیں۔ مثال کے طور پر، یہ پیٹرن اکثر ویب سرورز میں استعمال ہوتا ہے۔ ان پٹ پر، سرور کو صارف سے کچھ درخواست ہوتی ہے۔ یہ درخواست پھر پروسیسنگ چین سے گزرتی ہے۔ ہینڈلرز کے اس سلسلے میں فلٹرز (مثال کے طور پر، IP پتوں کی بلیک لسٹ سے درخواستیں قبول نہ کریں)، تصدیق کرنے والے ہینڈلرز (صرف مجاز صارفین کو اجازت دیں)، ایک درخواست ہیڈر ہینڈلر، کیشنگ ہینڈلر وغیرہ شامل ہیں۔ لیکن جاوا میں ایک آسان اور زیادہ قابل فہم مثال ہے java.util.logging:
import java.util.logging.*;
class Main {
  public static void main(String[] args) {
    Logger logger = Logger.getLogger(Main.class.getName());
    ConsoleHandler consoleHandler = new ConsoleHandler(){
		@Override
            public void publish(LogRecord record) {
                System.out.println("LogRecord обработан");
            }
        };
    logger.addHandler(consoleHandler);
    logger.info("test");
  }
}
جیسا کہ آپ دیکھ سکتے ہیں، ہینڈلرز کو لاگر ہینڈلرز کی فہرست میں شامل کیا جاتا ہے۔ جب ایک لاگر کو پروسیسنگ کے لیے کوئی پیغام موصول ہوتا ہے، تو اس طرح کا ہر پیغام logger.getHandlersاس لاگر کے لیے ہینڈلرز کی ایک زنجیر سے گزرتا ہے۔ ایک اور نمونہ جو ہم ہر روز دیکھتے ہیں وہ ہے " Iterator "۔ اس کا جوہر اشیاء کے مجموعے کو الگ کرنا ہے (یعنی ڈیٹا کے ڈھانچے کی نمائندگی کرنے والی کلاس۔ مثال کے طور پر، List) اور اس مجموعے کو عبور کرنا۔
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Iterator<String> iterator = data.iterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}
جیسا کہ آپ دیکھ سکتے ہیں، تکرار کرنے والا مجموعہ کا حصہ نہیں ہے، لیکن اس کی نمائندگی ایک علیحدہ طبقے سے ہوتی ہے جو مجموعہ کو عبور کرتی ہے۔ تکرار کرنے والے کے استعمال کنندہ کو یہ بھی معلوم نہیں ہوگا کہ وہ کس مجموعہ کو دہرا رہا ہے، یعنی وہ کس مجموعہ کا دورہ کر رہا ہے؟ وزیٹر پیٹرن بھی قابل غور ہے ۔ وزیٹر پیٹرن ایٹریٹر پیٹرن سے بہت ملتا جلتا ہے۔ یہ پیٹرن آپ کو اشیاء کی ساخت کو نظرانداز کرنے اور ان اشیاء پر کارروائیاں کرنے میں مدد کرتا ہے۔ وہ تصور میں بلکہ مختلف ہیں۔ تکرار کرنے والا مجموعہ کو عبور کرتا ہے تاکہ تکرار کرنے والا استعمال کرنے والے کلائنٹ کو اس بات کی پرواہ نہ ہو کہ مجموعہ اندر کیا ہے، صرف ترتیب میں موجود عناصر اہم ہیں۔ وزیٹر کا مطلب یہ ہے کہ ہم جن چیزوں کا دورہ کرتے ہیں ان کا ایک خاص درجہ بندی یا ڈھانچہ ہے۔ مثال کے طور پر، ہم علیحدہ ڈائریکٹری پروسیسنگ اور علیحدہ فائل پروسیسنگ استعمال کر سکتے ہیں۔ جاوا میں اس پیٹرن کا آؤٹ آف دی باکس نفاذ فارم میں ہے java.nio.file.FileVisitor:
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;
class Main {
  public static void main(String[] args) {
    SimpleFileVisitor visitor = new SimpleFileVisitor() {
      @Override
      public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException {
        System.out.println("File:" + file.toString());
        return FileVisitResult.CONTINUE;
      }
    };
    Path pathSource = Paths.get(System.getProperty("java.io.tmpdir"));
    try {
      Files.walkFileTree(pathSource, visitor);
    } catch (AccessDeniedException e) {
      // skip
    } catch (IOException e) {
      // Do something
    }
  }
}
بعض اوقات کچھ اشیاء کو دوسری اشیاء میں ہونے والی تبدیلیوں پر ردعمل ظاہر کرنے کی ضرورت ہوتی ہے، اور پھر "مبصر" پیٹرن ہماری مدد کرے گا ۔ سب سے آسان طریقہ یہ ہے کہ سبسکرپشن میکانزم فراہم کیا جائے جو کچھ اشیاء کو دیگر اشیاء میں پیش آنے والے واقعات کی نگرانی اور جواب دینے کی اجازت دیتا ہے۔ یہ نمونہ اکثر مختلف سامعین اور مبصرین میں استعمال ہوتا ہے جو مختلف واقعات پر ردعمل ظاہر کرتے ہیں۔ ایک سادہ مثال کے طور پر، ہم JDK کے پہلے ورژن سے اس پیٹرن کے نفاذ کو یاد کر سکتے ہیں:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Observer observer = (obj, arg) -> {
      System.out.println("Arg: " + arg);
    };
    Observable target = new Observable(){
      @Override
      public void notifyObservers(Object arg) {
        setChanged();
        super.notifyObservers(arg);
      }
    };
    target.addObserver(observer);
    target.notifyObservers("Hello, World!");
  }
}
ایک اور مفید طرز عمل ہے - " ثالث "۔ یہ مفید ہے کیونکہ پیچیدہ نظاموں میں یہ مختلف اشیاء کے درمیان تعلق کو دور کرنے اور اشیاء کے درمیان تمام تعاملات کو کسی چیز کے حوالے کرنے میں مدد کرتا ہے، جو کہ ایک درمیانی ہے۔ اس پیٹرن کی سب سے نمایاں ایپلی کیشنز میں سے ایک Spring MVC ہے، جو اس پیٹرن کو استعمال کرتی ہے۔ آپ اس کے بارے میں یہاں مزید پڑھ سکتے ہیں: " بہار: ثالثی پیٹرن "۔ آپ اکثر مثالوں میں اسی کو دیکھ سکتے ہیں java.util.Timer:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Timer mediator = new Timer("Mediator");
    TimerTask command = new TimerTask() {
      @Override
      public void run() {
        System.out.println("Command pattern");
        mediator.cancel();
      }
    };
    mediator.schedule(command, 1000);
  }
}
مثال زیادہ کمانڈ پیٹرن کی طرح نظر آتی ہے۔ اور "ثالث" پیٹرن کا جوہر Timer'a' کے نفاذ میں پوشیدہ ہے۔ ٹائمر کے اندر ایک ٹاسک کیو ہے TaskQueue، ایک دھاگہ ہے TimerThread۔ ہم، اس طبقے کے کلائنٹس کے طور پر، ان کے ساتھ تعامل نہیں کرتے، بلکہ Timerاس شے کے ساتھ تعامل کرتے ہیں، جو اس کے طریقوں پر ہماری کال کے جواب میں، دوسری اشیاء کے طریقوں تک رسائی حاصل کرتا ہے جن کا یہ ایک بیچوان ہے۔ بیرونی طور پر یہ "Facade" سے بہت مشابہت رکھتا ہے۔ لیکن فرق یہ ہے کہ جب ایک Facade استعمال کیا جاتا ہے، تو اجزاء نہیں جانتے کہ اگواڑا موجود ہے اور ایک دوسرے سے بات کرتے ہیں۔ اور جب "ثالث" کا استعمال کیا جاتا ہے، تو اجزاء بیچوان کو جانتے اور استعمال کرتے ہیں، لیکن ایک دوسرے سے براہ راست رابطہ نہیں کرتے۔ یہ " ٹیمپلیٹ میتھڈ " پیٹرن پر غور کرنے کے قابل ہے۔ پیٹرن اس کے نام سے واضح ہے۔ سب سے اہم بات یہ ہے کہ کوڈ کو اس طرح لکھا گیا ہے کہ کوڈ کے استعمال کنندگان (ڈیولپرز) کو کچھ الگورتھم ٹیمپلیٹ فراہم کیے گئے ہیں، جن کے مراحل کو دوبارہ بیان کرنے کی اجازت ہے۔ یہ کوڈ استعمال کرنے والوں کو پورا الگورتھم لکھنے کی اجازت نہیں دیتا ہے، بلکہ صرف اس بارے میں سوچنے کی اجازت دیتا ہے کہ اس الگورتھم کے ایک یا دوسرے مرحلے کو صحیح طریقے سے کیسے انجام دیا جائے۔ مثال کے طور پر، جاوا میں ایک تجریدی کلاس ہے AbstractListجو ایک تکرار کرنے والے کے رویے کی وضاحت کرتی ہے List۔ تاہم، تکرار کرنے والا خود پتی کے طریقے استعمال کرتا ہے جیسے: get, set, remove. ان طریقوں کے رویے کا تعین اولاد کے ڈویلپر کے ذریعے کیا جاتا ہے AbstractList۔ اس طرح، iterator in AbstractList- ایک شیٹ پر تکرار کرنے کے لیے الگورتھم کے لیے ایک ٹیمپلیٹ ہے۔ اور مخصوص نفاذ کے ڈویلپر AbstractListمخصوص اقدامات کے رویے کی وضاحت کرکے اس تکرار کے رویے کو تبدیل کرتے ہیں۔ ہم جن نمونوں کا تجزیہ کرتے ہیں ان میں سے آخری " اسنیپ شاٹ " (مومینٹو) پیٹرن ہے۔ اس کا جوہر اس حالت کو بحال کرنے کی صلاحیت کے ساتھ کسی چیز کی ایک خاص حالت کو محفوظ رکھنا ہے۔ JDK کی سب سے زیادہ قابل شناخت مثال آبجیکٹ سیریلائزیشن ہے، یعنی java.io.Serializable. آئیے ایک مثال دیکھتے ہیں:
import java.io.*;
import java.util.*;
class Main {
  public static void main(String[] args) throws IOException {
    ArrayList<String> list = new ArrayList<>();
    list.add("test");
    // Save State
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    try (ObjectOutputStream out = new ObjectOutputStream(stream)) {
      out.writeObject(list);
    }
    // Load state
    byte[] bytes = stream.toByteArray();
    InputStream inputStream = new ByteArrayInputStream(bytes);
    try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
      List<String> listNew = (List<String>) in.readObject();
      System.out.println(listNew.get(0));
    } catch (ClassNotFoundException e) {
      // Do something. Can't find class fpr saved state
    }
  }
}
جاوا میں ڈیزائن پیٹرن - 8

نتیجہ

جیسا کہ ہم نے جائزہ سے دیکھا، پیٹرن کی ایک بہت بڑی قسم ہے۔ ان میں سے ہر ایک اپنا مسئلہ خود حل کرتا ہے۔ اور ان نمونوں کا علم آپ کو وقت پر یہ سمجھنے میں مدد دے سکتا ہے کہ آپ کے سسٹم کو کیسے لکھنا ہے تاکہ یہ لچکدار، برقرار رکھنے کے قابل اور تبدیلی کے خلاف مزاحم ہو۔ اور آخر میں، گہرے غوطے کے لیے کچھ لنکس: #ویاچسلاو
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION