CodeGym /Java Blog /এলোমেলো /বিরোধী নিদর্শন কি? আসুন কিছু উদাহরণ দেখি (পর্ব 1)
John Squirrels
লেভেল 41
San Francisco

বিরোধী নিদর্শন কি? আসুন কিছু উদাহরণ দেখি (পর্ব 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. একটি সিঙ্গলটন সলিড নীতিগুলি লঙ্ঘন করে — একক দায়িত্ব নীতি: তার প্রত্যক্ষ দায়িত্ব ছাড়াও, সিঙ্গেলটন শ্রেণীও দৃষ্টান্তের সংখ্যা নিয়ন্ত্রণ করে।

  3. একটি সিঙ্গলটনের উপর একটি সাধারণ শ্রেণীর নির্ভরতা ক্লাসের ইন্টারফেসে দৃশ্যমান নয়। যেহেতু একটি সিঙ্গেলটন ইনস্ট্যান্স সাধারণত মেথড আর্গুমেন্ট হিসাবে পাস করা হয় না, বরং সরাসরি getInstance() এর মাধ্যমে প্রাপ্ত হয়, আপনাকে সিঙ্গেলটনের উপর ক্লাসের নির্ভরতা শনাক্ত করার জন্য প্রতিটি পদ্ধতির বাস্তবায়নে প্রবেশ করতে হবে — শুধুমাত্র একটি ক্লাসের জনসাধারণের দিকে তাকিয়ে চুক্তি যথেষ্ট নয়।

    একটি সিঙ্গলটনের উপস্থিতি সামগ্রিকভাবে অ্যাপ্লিকেশনের পরীক্ষাযোগ্যতা এবং বিশেষভাবে সিঙ্গেলটন ব্যবহার করে এমন ক্লাসগুলিকে হ্রাস করে। প্রথমত, আপনি সিঙ্গলটনকে একটি মক অবজেক্ট দিয়ে প্রতিস্থাপন করতে পারবেন না। দ্বিতীয়ত, যদি একটি সিঙ্গলটনের অবস্থা পরিবর্তন করার জন্য একটি ইন্টারফেস থাকে, তাহলে পরীক্ষাগুলি একে অপরের উপর নির্ভর করবে।

    অন্য কথায়, একটি সিঙ্গলটন কাপলিং বাড়ায় এবং উপরে উল্লিখিত সবকিছুই বর্ধিত কাপলিং এর পরিণতি ছাড়া আর কিছুই নয়।

    এবং যদি আপনি এটি সম্পর্কে চিন্তা করেন, আপনি একটি সিঙ্গেলটন ব্যবহার এড়াতে পারেন। উদাহরণস্বরূপ, একটি বস্তুর দৃষ্টান্তের সংখ্যা নিয়ন্ত্রণ করতে বিভিন্ন ধরণের কারখানা ব্যবহার করা বেশ সম্ভব (এবং প্রকৃতপক্ষে প্রয়োজনীয়)।

    সিঙ্গেলটনের উপর ভিত্তি করে একটি সম্পূর্ণ অ্যাপ্লিকেশন আর্কিটেকচার তৈরি করার প্রচেষ্টায় সবচেয়ে বড় বিপদ রয়েছে। এই পদ্ধতির জন্য বিস্ময়কর বিকল্প টন আছে. সবচেয়ে গুরুত্বপূর্ণ উদাহরণ হল স্প্রিং, অর্থাৎ এর আইওসি কন্টেইনারগুলি: এগুলি পরিষেবা তৈরির নিয়ন্ত্রণের সমস্যার একটি প্রাকৃতিক সমাধান, যেহেতু তারা আসলে "স্টেরয়েডের কারখানা"।

    অনেক অন্তহীন এবং অমীমাংসিত বিতর্ক এখন এই বিষয়ে রাগ হয়. একটি সিঙ্গলটন একটি প্যাটার্ন বা অ্যান্টি-প্যাটার্ন কিনা তা সিদ্ধান্ত নেওয়া আপনার উপর নির্ভর করে।

    আমরা এটা নিয়ে দেরি করব না। পরিবর্তে, আমরা আজকের জন্য শেষ ডিজাইন প্যাটার্নে চলে যাব — পোল্টারজিস্ট।

4. Poltergeist

একটি পোল্টারজিস্ট হল একটি বিরোধী প্যাটার্ন যা একটি অর্থহীন শ্রেণীকে জড়িত করে যা অন্য শ্রেণীর পদ্ধতিকে কল করতে বা কেবল বিমূর্ততার একটি অপ্রয়োজনীয় স্তর যোগ করতে ব্যবহৃত হয়। এই অ্যান্টি-প্যাটার্ন স্বল্পস্থায়ী বস্তু হিসাবে নিজেকে প্রকাশ করে, রাষ্ট্র বর্জিত। এই বস্তুগুলি প্রায়ই অন্যান্য, আরও স্থায়ী বস্তুর সূচনা করতে ব্যবহৃত হয়।

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());
}
আমাদের কাছে একটি আপডেট পদ্ধতি রয়েছে যা ডেটাবেস থেকে ব্যবহারকারীর ডেটাকে মেথডে পাস করা ব্যবহারকারীর ডেটার সাথে একত্রিত করার জন্য একটি পৃথক পদ্ধতি ব্যবহার করে (যদি আপডেট পদ্ধতিতে পাস করা ব্যবহারকারীর একটি নাল ফিল্ড থাকে, তাহলে পুরানো ক্ষেত্রের মান ডাটাবেস থেকে নেওয়া হয়) . তারপর ধরুন একটি নতুন প্রয়োজনীয়তা রয়েছে যে রেকর্ডগুলি অবশ্যই পুরানোগুলির সাথে একত্রিত করা উচিত নয়, তবে পরিবর্তে, নাল ক্ষেত্র থাকলেও, তারা পুরানোগুলিকে ওভাররাইট করতে ব্যবহৃত হয়:

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);
   }
}
এখানে সবকিছুই যথেষ্ট পরিষ্কার: আমরা একটি পুল অবজেক্ট পাই, পুল থেকে রিসোর্স সহ একটি অবজেক্ট পাই, রিসোর্স অবজেক্ট থেকে ম্যাপ পাই, এটি দিয়ে কিছু করি এবং পুলটিতে আরও পুনঃব্যবহারের জন্য এই সমস্ত কিছু রাখি। ভয়েলা, এটি হল অবজেক্ট পুল ডিজাইন প্যাটার্ন। কিন্তু আমরা বিরোধী নিদর্শন সম্পর্কে কথা বলছিলাম, তাই না? আসুন প্রধান পদ্ধতিতে নিম্নলিখিত ক্ষেত্রে বিবেচনা করা যাক:

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);
}
মানচিত্রের বিষয়বস্তু পরিবর্তন করার প্রচেষ্টা এবং ইচ্ছা ম্লান হয়ে যাবে যা তারা তৈরি করবে অসমর্থিত অপারেশন ব্যতিক্রমের জন্য ধন্যবাদ। অ্যান্টি-প্যাটার্নগুলি হল ফাঁদ যা ডেভেলপাররা ঘন ঘন সময়ের তীব্র অভাব, অসাবধানতা, অনভিজ্ঞতা বা প্রকল্প পরিচালকদের চাপের কারণে সম্মুখীন হয়। তাড়াহুড়ো, যা সাধারণ, ভবিষ্যতে অ্যাপ্লিকেশনের জন্য বড় সমস্যা হতে পারে, তাই আপনাকে এই ত্রুটিগুলি সম্পর্কে জানতে হবে এবং সেগুলি এড়িয়ে চলতে হবে। এটি নিবন্ধের প্রথম অংশটি শেষ করে। চলবে...
মন্তব্য
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION