CodeGym /جاوا بلاگ /Random-UR /اینٹی پیٹرن کیا ہیں؟ آئیے کچھ مثالیں دیکھتے ہیں (حصہ 1)
John Squirrels
سطح
San Francisco

اینٹی پیٹرن کیا ہیں؟ آئیے کچھ مثالیں دیکھتے ہیں (حصہ 1)

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

1. تجزیاتی فالج

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

2. خدا کا اعتراض

ایک خدا آبجیکٹ ایک اینٹی پیٹرن ہے جو تمام قسم کے افعال کی ضرورت سے زیادہ ارتکاز اور مختلف اعداد و شمار کی بڑی مقدار (ایک ایسی چیز جس کے گرد ایپلی کیشن گھومتی ہے) کو بیان کرتی ہے۔ ایک چھوٹی سی مثال لیں:
public class SomeUserGodObject {
   private static final String FIND_ALL_USERS_EN = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users;
   private static final String FIND_BY_ID = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users WHERE id = ?";
   private static final String FIND_ALL_CUSTOMERS = "SELECT id, u.email, u.phone, u.first_name_en, u.middle_name_en, u.last_name_en, u.created_date" +
           "  WHERE u.id IN (SELECT up.user_id FROM user_permissions up WHERE up.permission_id = ?)";
   private static final String FIND_BY_EMAIL = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_dateFROM users WHERE email = ?";
   private static final String LIMIT_OFFSET = " LIMIT ? OFFSET ?";
   private static final String ORDER = " ORDER BY ISNULL(last_name_en), last_name_en, ISNULL(first_name_en), first_name_en, ISNULL(last_name_ru), " +
           "last_name_ru, ISNULL(first_name_ru), first_name_ru";
   private static final String CREATE_USER_EN = "INSERT INTO users(id, phone, email, first_name_en, middle_name_en, last_name_en, created_date) " +
           "VALUES (?, ?, ?, ?, ?, ?, ?)";
   private static final String FIND_ID_BY_LANG_CODE = "SELECT id FROM languages WHERE lang_code = ?";
                                  ........
   private final JdbcTemplate jdbcTemplate;
   private Map<String, String> firstName;
   private Map<String, String> middleName;
   private Map<String, String> lastName;
   private List<Long> permission;
                                   ........
   @Override
   public List<User> findAllEnCustomers(Long permissionId) {
       return jdbcTemplate.query( FIND_ALL_CUSTOMERS + ORDER, userRowMapper(), permissionId);
   }
   @Override
   public List<User> findAllEn() {
       return jdbcTemplate.query(FIND_ALL_USERS_EN + ORDER, userRowMapper());
   }
   @Override
   public Optional<List<User>> findAllEnByEmail(String email) {
       var query = FIND_ALL_USERS_EN + FIND_BY_EMAIL + ORDER;
       return Optional.ofNullable(jdbcTemplate.query(query, userRowMapper(), email));
   }
                              .............
   private List<User> findAllWithoutPageEn(Long permissionId, Type type) {
       switch (type) {
           case USERS:
               return findAllEnUsers(permissionId);
           case CUSTOMERS:
               return findAllEnCustomers(permissionId);
           default:
               return findAllEn();
       }
   }
                              ..............private RowMapper<User> userRowMapperEn() {
       return (rs, rowNum) ->
               User.builder()
                       .id(rs.getLong("id"))
                       .email(rs.getString("email"))
                       .accessFailed(rs.getInt("access_counter"))
                       .createdDate(rs.getObject("created_date", LocalDateTime.class))
                       .firstName(rs.getString("first_name_en"))
                       .middleName(rs.getString("middle_name_en"))
                       .lastName(rs.getString("last_name_en"))
                       .phone(rs.getString("phone"))
                       .build();
   }
}
یہاں ہم ایک بہت بڑا طبقہ دیکھتے ہیں جو سب کچھ کرتا ہے۔ اس میں ڈیٹا بیس کے سوالات کے ساتھ ساتھ کچھ ڈیٹا بھی ہوتا ہے۔ ہم findAllWithoutPageEn اگواڑا طریقہ بھی دیکھتے ہیں، جس میں کاروباری منطق شامل ہے۔ اس طرح کی خدا کی چیز بہت بڑا اور مناسب طریقے سے برقرار رکھنے کے لئے عجیب ہو جاتا ہے. ہمیں کوڈ کے ہر ٹکڑے میں اس کے ساتھ گڑبڑ کرنا ہوگی۔ سسٹم کے بہت سے اجزاء اس پر انحصار کرتے ہیں اور اس کے ساتھ مضبوطی سے جڑے ہوئے ہیں۔ اس طرح کے کوڈ کو برقرار رکھنا مشکل سے مشکل تر ہوتا جاتا ہے۔ ایسے معاملات میں، کوڈ کو الگ الگ کلاسوں میں تقسیم کیا جانا چاہیے، جن میں سے ہر ایک کا صرف ایک مقصد ہوگا۔ اس مثال میں، ہم خدا آبجیکٹ کو ڈاؤ کلاس میں تقسیم کر سکتے ہیں:
public class UserDaoImpl {
   private static final String FIND_ALL_USERS_EN = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users;
   private static final String FIND_BY_ID = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users WHERE id = ?";

                                   ........
   private final JdbcTemplate jdbcTemplate;

                                   ........
   @Override
   public List<User> findAllEnCustomers(Long permissionId) {
       return jdbcTemplate.query(FIND_ALL_CUSTOMERS + ORDER, userRowMapper(), permissionId);
   }
   @Override
   public List<User> findAllEn() {
       return jdbcTemplate.query(FIND_ALL_USERS_EN + ORDER, userRowMapper());
   }

                               ........
}
ڈیٹا تک رسائی کے لیے ڈیٹا اور طریقوں پر مشتمل ایک کلاس:
public class UserInfo {
   private Map<String, String> firstName;..
   public Map<String, String> getFirstName() {
       return firstName;
   }
   public void setFirstName(Map<String, String> firstName) {
       this.firstName = firstName;
   }
                    ....
اور کاروباری منطق کے ساتھ طریقہ کار کو سروس میں منتقل کرنا زیادہ مناسب ہوگا:
private List<User> findAllWithoutPageEn(Long permissionId, Type type) {
   switch (type) {
       case USERS:
           return findAllEnUsers(permissionId);
       case CUSTOMERS:
           return findAllEnCustomers(permissionId);
       default:
           return findAllEn();
   }
}

3. سنگلٹن

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

  2. سنگلٹن SOLID اصولوں کی خلاف ورزی کرتا ہے - واحد ذمہ داری کا اصول: اپنے براہ راست فرائض کے علاوہ، سنگلٹن کلاس مثالوں کی تعداد کو بھی کنٹرول کرتی ہے۔

  3. ایک عام طبقے کا سنگلٹن پر انحصار کلاس کے انٹرفیس میں نظر نہیں آتا۔ چونکہ سنگلٹن مثال عام طور پر طریقہ دلیل کے طور پر منظور نہیں کی جاتی ہے، بلکہ اس کے بجائے براہ راست getInstance() کے ذریعے حاصل کی جاتی ہے، آپ کو سنگلٹن پر کلاس کے انحصار کی نشاندہی کرنے کے لیے ہر طریقہ کے نفاذ میں داخل ہونے کی ضرورت ہے — صرف ایک کلاس کے عوام کو دیکھتے ہوئے معاہدہ کافی نہیں ہے.

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

    دوسرے لفظوں میں، سنگلٹن جوڑے کو بڑھاتا ہے، اور اوپر بیان کردہ ہر چیز جوڑے کے بڑھنے کے نتیجے سے زیادہ کچھ نہیں ہے۔

    اور اگر آپ اس کے بارے میں سوچتے ہیں، تو آپ سنگلٹن کے استعمال سے بچ سکتے ہیں۔ مثال کے طور پر، کسی چیز کی مثالوں کی تعداد کو کنٹرول کرنے کے لیے مختلف قسم کے کارخانوں کا استعمال کرنا کافی ممکن ہے (اور درحقیقت ضروری)۔

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

    اس موضوع پر اب بہت سی غیر متضاد اور ناقابل مصالحت بحثیں چل رہی ہیں۔ یہ آپ پر منحصر ہے کہ آیا سنگلٹن پیٹرن ہے یا اینٹی پیٹرن۔

    ہم اس پر دیر نہیں کریں گے۔ اس کے بجائے، ہم آج کے آخری ڈیزائن پیٹرن پر جائیں گے — poltergeist۔

4. پولٹرجیسٹ

پولٹرجسٹ ایک اینٹی پیٹرن ہے جس میں ایک بے معنی کلاس شامل ہے جو کسی دوسرے طبقے کے طریقوں کو کال کرنے کے لئے استعمال کیا جاتا ہے یا محض تجرید کی ایک غیر ضروری پرت شامل کرتا ہے۔ یہ مخالف پیٹرن اپنے آپ کو قلیل المدتی اشیاء کے طور پر ظاہر کرتا ہے، ریاست سے خالی۔ یہ اشیاء اکثر دوسری، زیادہ مستقل اشیاء کو شروع کرنے کے لیے استعمال ہوتی ہیں۔
public class UserManager {
   private UserService service;
   public UserManager(UserService userService) {
       service = userService;
   }
   User createUser(User user) {
       return service.create(user);
   }
   Long findAllUsers(){
       return service.findAll().size();
   }
   String findEmailById(Long id) {
       return service.findById(id).getEmail();}
   User findUserByEmail(String email) {
       return service.findByEmail(email);
   }
   User deleteUserById(Long id) {
       return service.delete(id);
   }
}
ہمیں ایسی چیز کی ضرورت کیوں ہے جو صرف ایک ثالث ہو اور اپنا کام کسی اور کو سونپے؟ ہم اسے ختم کرتے ہیں اور اس کی تھوڑی سی فعالیت کو طویل عرصے تک رہنے والی اشیاء میں منتقل کرتے ہیں۔ اس کے بعد، ہم ان نمونوں کی طرف بڑھتے ہیں جو ہمارے لیے سب سے زیادہ دلچسپی رکھتے ہیں (بطور عام ڈویلپرز)، یعنی ترقی مخالف پیٹرن ۔

5. سخت کوڈنگ

تو ہم اس خوفناک لفظ پر پہنچ گئے ہیں: سخت کوڈنگ۔ اس اینٹی پیٹرن کا جوہر یہ ہے کہ کوڈ ایک مخصوص ہارڈویئر کنفیگریشن اور/یا سسٹم کے ماحول سے مضبوطی سے جڑا ہوا ہے۔ یہ کوڈ کو دیگر کنفیگریشنز میں پورٹ کرنے میں بہت زیادہ پیچیدہ بناتا ہے۔ یہ اینٹی پیٹرن جادوئی نمبروں کے ساتھ قریب سے وابستہ ہے (یہ مخالف پیٹرن اکثر آپس میں جڑے ہوتے ہیں)۔ مثال:
public Connection buildConnection() throws Exception {
   Class.forName("com.mysql.cj.jdbc.Driver");
   connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/someDb?characterEncoding=UTF-8&characterSetResults=UTF-8&serverTimezone=UTC", "user01", "12345qwert");
   return connection;
}
درد ہوتا ہے، ہے نا؟ یہاں ہم اپنے کنکشن کی ترتیبات کو ہارڈ کوڈ کرتے ہیں۔ نتیجے کے طور پر، کوڈ صرف MySQL کے ساتھ صحیح طریقے سے کام کرے گا۔ ڈیٹا بیس کو تبدیل کرنے کے لیے، ہمیں کوڈ میں غوطہ لگانے اور ہر چیز کو دستی طور پر تبدیل کرنے کی ضرورت ہوگی۔ ایک اچھا حل یہ ہوگا کہ کنفیگریشن کو الگ فائل میں ڈال دیا جائے:
spring:
  datasource:
    jdbc-url:jdbc:mysql://localhost:3306/someDb?characterEncoding=UTF-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username:  user01
    password:  12345qwert
دوسرا آپشن مستقل استعمال کرنا ہے۔

6. کشتی کا لنگر

اینٹی پیٹرن کے تناظر میں، بوٹ اینکر کا مطلب ہے سسٹم کے ان حصوں کو رکھنا جو کچھ اصلاح یا ری فیکٹرنگ کرنے کے بعد مزید استعمال نہیں ہوتے ہیں۔ نیز، کوڈ کے کچھ حصوں کو "مستقبل کے استعمال کے لیے" رکھا جا سکتا ہے صرف اس صورت میں جب آپ کو اچانک ان کی ضرورت پڑ جائے۔ بنیادی طور پر، یہ آپ کے کوڈ کو کوڑے دان میں بدل دیتا ہے۔ مثال:
public User update(Long id, User request) {
   User user = mergeUser(findById(id), request);
   return userDAO.update(user);
}
private User mergeUser(User findUser, User requestUser) {
   return new User(
           findUser.getId(),
           requestUser.getEmail() != null ? requestUser.getEmail() : findUser.getEmail(),
           requestUser.getFirstName() != null ? requestUser.getFirstName() : findUser.getFirstNameRu(),
           requestUser.getMiddleName() != null ? requestUser.getMiddleName() : findUser.getMiddleNameRu(),
           requestUser.getLastName() != null ? requestUser.getLastName() : findUser.getLastNameEn(),
           requestUser.getPhone() != null ? requestUser.getPhone() : findUser.getPhone());
}
ہمارے پاس ایک اپ ڈیٹ کا طریقہ ہے جو ڈیٹا بیس سے صارف کے ڈیٹا کو میتھڈ میں بھیجے گئے صارف کے ڈیٹا کے ساتھ ضم کرنے کے لیے ایک الگ طریقہ استعمال کرتا ہے (اگر اپ ڈیٹ کے طریقہ کار کو پاس کیے گئے صارف کے پاس نل فیلڈ ہے، تو پرانی فیلڈ ویلیو ڈیٹا بیس سے لی جاتی ہے) . پھر فرض کریں کہ ایک نئی ضرورت ہے کہ ریکارڈز کو پرانے کے ساتھ ضم نہیں کیا جانا چاہیے، لیکن اس کے بجائے، یہاں تک کہ اگر null فیلڈز ہیں، تو وہ پرانے کو اوور رائٹ کرنے کے لیے استعمال ہوتے ہیں:
public User update(Long id, User request) {
   return userDAO.update(user);
}
اس کا مطلب یہ ہے کہ mergeUser اب استعمال نہیں کیا جاتا ہے، لیکن اسے حذف کرنا افسوس کی بات ہو گی — اگر یہ طریقہ (یا اس طریقہ کار کا خیال) کسی دن کام آ جائے تو کیا ہوگا؟ اس طرح کا کوڈ صرف نظاموں کو پیچیدہ بناتا ہے اور الجھن کو متعارف کرواتا ہے، جس کی بنیادی طور پر کوئی عملی قدر نہیں ہوتی۔ ہمیں یہ نہیں بھولنا چاہیے کہ "مردہ ٹکڑوں" کے ساتھ اس طرح کے کوڈ کو کسی ساتھی کو منتقل کرنا مشکل ہو گا جب آپ کسی دوسرے پروجیکٹ کے لیے روانہ ہوں گے۔ کشتی کے اینکرز سے نمٹنے کا بہترین طریقہ یہ ہے کہ کوڈ کو ری ایکٹر کیا جائے، یعنی کوڈ کے سیکشنز کو حذف کریں (دل دہلا دینے والا، میں جانتا ہوں)۔ مزید برآں، ترقیاتی شیڈول تیار کرتے وقت، ایسے اینکرز کا محاسبہ کرنا ضروری ہے (صاف کرنے کے لیے وقت مختص کرنے کے لیے)۔

7. آبجیکٹ سیسپول

اس اینٹی پیٹرن کو بیان کرنے کے لیے، پہلے آپ کو آبجیکٹ پول پیٹرن سے واقفیت حاصل کرنے کی ضرورت ہے۔ ایک آبجیکٹ پول (وسائل پول) ایک تخلیقی ڈیزائن پیٹرن ہے ، ابتدائی اور استعمال کے لیے تیار اشیاء کا ایک سیٹ۔ جب کسی ایپلی کیشن کو کسی چیز کی ضرورت ہوتی ہے، تو اسے دوبارہ بنانے کے بجائے اس پول سے لیا جاتا ہے۔ جب کسی چیز کی ضرورت نہیں رہتی ہے تو وہ تباہ نہیں ہوتی ہے۔ اس کے بجائے، یہ پول میں واپس آ گیا ہے. یہ پیٹرن عام طور پر بھاری اشیاء کے لیے استعمال کیا جاتا ہے جو ہر وقت ضرورت کے وقت تخلیق کرنے میں وقت لگتی ہیں، جیسے کہ ڈیٹا بیس سے منسلک ہوتے وقت۔ آئیے ایک چھوٹی اور سادہ مثال دیکھتے ہیں۔ یہاں ایک کلاس ہے جو اس پیٹرن کی نمائندگی کرتی ہے:
class ReusablePool {
   private static ReusablePool pool;
   private List<Resource> list = new LinkedList<>();
   private ReusablePool() {
       for (int i = 0; i < 3; i++)
           list.add(new Resource());
   }
   public static ReusablePool getInstance() {
       if (pool == null) {
           pool = new ReusablePool();
       }
       return pool;
   }
   public Resource acquireResource() {
       if (list.size() == 0) {
           return new Resource();
       } else {
           Resource r = list.get(0);
           list.remove(r);
           return r;
       }
   }
   public void releaseResource(Resource r) {
       list.add(r);
   }
}
یہ کلاس مندرجہ بالا سنگلٹن پیٹرن/اینٹی پیٹرن کی شکل میں پیش کی گئی ہے ، یعنی اس قسم کی صرف ایک چیز ہوسکتی ہے۔ یہ بعض Resourceاشیاء کا استعمال کرتا ہے. پہلے سے طے شدہ طور پر، کنسٹرکٹر پول کو 4 مثالوں سے بھرتا ہے۔ جب آپ کو کوئی شے ملتی ہے، تو اسے پول سے ہٹا دیا جاتا ہے (اگر کوئی چیز دستیاب نہیں ہے تو، ایک بنائی جاتی ہے اور فوری طور پر واپس کردی جاتی ہے)۔ اور آخر میں، ہمارے پاس آبجیکٹ کو واپس رکھنے کا طریقہ ہے۔ وسائل کی اشیاء اس طرح نظر آتی ہیں:
public class Resource {
   private Map<String, String> patterns;
   public Resource() {
       patterns = new HashMap<>();
       patterns.put("proxy", "https://en.wikipedia.org/wiki/Proxy_pattern");
       patterns.put("bridge", "https://en.wikipedia.org/wiki/Bridge_pattern");
       patterns.put("facade", "https://en.wikipedia.org/wiki/Facade_pattern");
       patterns.put("builder", "https://en.wikipedia.org/wiki/Builder_pattern");
   }
   public Map<String, String> getPatterns() {
       return patterns;
   }
   public void setPatterns(Map<String, String> patterns) {
       this.patterns = patterns;
   }
}
یہاں ہمارے پاس ایک چھوٹی آبجیکٹ ہے جس میں ڈیزائن پیٹرن کے ناموں کے ساتھ ایک نقشہ ہے جس میں کلید کے طور پر اور متعلقہ ویکیپیڈیا لنکس قدر کے ساتھ ساتھ نقشے تک رسائی کے طریقے ہیں۔ آئیے اہم پر ایک نظر ڈالیں:
class SomeMain {
   public static void main(String[] args) {
       ReusablePool pool = ReusablePool.getInstance();

       Resource firstResource = pool.acquireResource();
       Map<String, String> firstPatterns = firstResource.getPatterns();
       // use our map somehow...
       pool.releaseResource(firstResource);

       Resource secondResource = pool.acquireResource();
       Map<String, String> secondPatterns = firstResource.getPatterns();
       // use our map somehow...
       pool.releaseResource(secondResource);

       Resource thirdResource = pool.acquireResource();
       Map<String, String> thirdPatterns = firstResource.getPatterns();
       // use our map somehow...
       pool.releaseResource(thirdResource);
   }
}
یہاں سب کچھ واضح ہے: ہمیں ایک پول آبجیکٹ ملتا ہے، پول سے وسائل کے ساتھ ایک آبجیکٹ ملتا ہے، ریسورس آبجیکٹ سے نقشہ حاصل ہوتا ہے، اس کے ساتھ کچھ کرتے ہیں، اور یہ سب کچھ اس کی جگہ پر مزید دوبارہ استعمال کے لیے پول میں رکھتے ہیں۔ Voila، یہ آبجیکٹ پول ڈیزائن پیٹرن ہے. لیکن ہم مخالف پیٹرن کے بارے میں بات کر رہے تھے، ٹھیک ہے؟ آئیے اہم طریقہ میں درج ذیل صورت پر غور کریں:
Resource fourthResource = pool.acquireResource();
   Map<String, String> fourthPatterns = firstResource.getPatterns();
// use our map somehow...
fourthPatterns.clear();
firstPatterns.put("first","blablabla");
firstPatterns.put("second","blablabla");
firstPatterns.put("third","blablabla");
firstPatterns.put("fourth","blablabla");
pool.releaseResource(fourthResource);
یہاں، دوبارہ، ہمیں ایک ریسورس آبجیکٹ ملتا ہے، ہمیں اس کے نمونوں کا نقشہ ملتا ہے، اور ہم نقشے کے ساتھ کچھ کرتے ہیں۔ لیکن نقشہ کو آبجیکٹ کے تالاب میں واپس محفوظ کرنے سے پہلے، اسے صاف کیا جاتا ہے اور پھر کرپٹ ڈیٹا سے آباد کیا جاتا ہے، جس سے ریسورس آبجیکٹ دوبارہ استعمال کے لیے موزوں نہیں ہوتا ہے۔ آبجیکٹ پول کی اہم تفصیلات میں سے ایک یہ ہے کہ جب کوئی چیز واپس کی جاتی ہے، تو اسے دوبارہ استعمال کے لیے موزوں حالت میں بحال ہونا چاہیے۔ اگر پول میں واپس آنے والی اشیاء غلط یا غیر وضاحتی حالت میں رہتی ہیں، تو ہمارے ڈیزائن کو آبجیکٹ سیسپول کہا جاتا ہے۔ کیا ایسی اشیاء کو ذخیرہ کرنا کوئی معنی رکھتا ہے جو دوبارہ استعمال کے لیے موزوں نہیں ہیں؟ اس صورت حال میں، ہم کنسٹرکٹر میں اندرونی نقشے کو ناقابل تغیر بنا سکتے ہیں:
public Resource() {
   patterns = new HashMap<>();
   patterns.put("proxy", "https://en.wikipedia.org/wiki/Proxy_pattern");
   patterns.put("bridge", "https://en.wikipedia.org/wiki/Bridge_pattern");
   patterns.put("facade", "https://en.wikipedia.org/wiki/Facade_pattern");
   patterns.put("builder", "https://en.wikipedia.org/wiki/Builder_pattern");
   patterns = Collections.unmodifiableMap(patterns);
}
نقشے کے مشمولات کو تبدیل کرنے کی کوششیں اور خواہش ختم ہو جائیں گی ان کے پیدا کردہ UnsupportedOperationException کی بدولت۔ اینٹی پیٹرن ایسے جال ہیں جن کا سامنا وقت کی شدید کمی، لاپرواہی، ناتجربہ کاری، یا پروجیکٹ مینیجرز کے دباؤ کی وجہ سے ڈویلپرز کو اکثر ہوتا ہے۔ جلدی کرنا، جو عام بات ہے، مستقبل میں ایپلیکیشن کے لیے بڑی پریشانیوں کا باعث بن سکتی ہے، اس لیے آپ کو ان غلطیوں کے بارے میں پہلے سے جاننا اور ان سے بچنا چاہیے۔ یہ مضمون کا پہلا حصہ ختم کرتا ہے۔ جاری ہے...
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION