Що таке антипатерни?  Розбираємо приклади (частина 1) - 1Всім доброго доби! Днями я проходив співбесіду, і мені поставабо питання про антипаттерни: що це за звір такий, які бувають їхні види та приклади на практиці. На запитання я, звичайно, відповів, але дуже поверхово, тому що не сильно заглиблювався у вивчення цього питання. Після співбесіди я почав нишпорити по просторах інтернету, дедалі більше занурюючись у цю тему. Сьогодні я хотів би зробити невеликий огляд найпопулярніших антипаттернів та їх прикладів, прочитання якого, можливо, дасть вам необхідні знання у цьому питанні. Почнемо! Отже, як міркувати, що таке антипаттерн, давайте згадаємо, що таке патерн. Паттерн- Це повторна архітектурна конструкція для вирішення часто зустрічаються проблем або ситуацій, що виникають при проектуванні програми. Але сьогодні у нас йдеться не про них, а про їхні протилежності — антипатерни. Антипаттерн - це поширений підхід до вирішення класу проблем, що часто зустрічаються, який є неефективним, ризикованим або непродуктивним. Інакше висловлюючись, це патерн помилок (також іноді званий пасткою). Що таке антипатерни?  Розбираємо приклади (частина 1) - 2Як правило, антипатерни ділять на такі види:
  1. Architectural antipatterns - антипатерни архітектури, що виникають при проектуванні структури системи (як правило архітектором).
  2. Management Anti Pattern - антипатерни в галузі управління, з якими зазвичай стикаються різноманітні менеджери (або групи менеджерів).
  3. Development Anti Pattern - антипатерні проблеми розробки, що виникають під час написання системи рядовими програмістами.
Екзотика антипатернів набагато ширша, але ми їх розглядати сьогодні не будемо, тому що для звичайних розробників цього буде з головою. Спочатку як приклад розглянемо антипаттерн у сфері управління.

1. Analytical paralysis

Аналітичний параліч- Вважається класичним організаційним антипаттерном. Його суть полягає в надмірному аналізуванні ситуації при плануванні, так що рішення чи дія не робляться, по суті, паралізуючи розробку. Найчастіше це трапляється у тих випадках, коли мета полягає у досягненні досконалості та повної завершеності періоду аналізу. Цей антипаттерн характеризується ходінням по колу (такий собі замкнутий цикл), переглядом та створенням детальних моделей, що у свою чергу заважає робочому процесу. Наприклад, ви намагаєтеся передбачити речі рівня: а якщо раптом користувач захоче створити список співробітників на основі четвертих і п'ятих літер їх імені, з включенням до списку проектів, яким вони приділабо найбільше робочих годин між Новим Роком і Восьмим березня за чотири попередні роки ? Насправді це надлишок аналізу.привів Kodak до банкрутства . Ось пара невеликих порад для боротьби аналітичним паралічем:
  1. Потрібно визначити довгострокову мету як маяк для прийняття рішень, щоб кожне ваше рішення наближало до мети, а не змушувало тупцювати на місці.
  2. Не концентруватися на дрібницях (навіщо приймати рішення щодо незначного нюансу так, ніби воно останнє у житті?)
  3. Вкажіть крайній термін для прийняття рішення.
  4. Не намагайтеся зробити завдання досконало: краще зробити дуже добре.
Занадто поглиблюватись і розглядати інші управлінські антипатерни ми зараз не будемо. Тому без передмов переходимо до деяких архітектурних антипаттернів, адже, швидше за все, цю статтю читають майбутні розробники, а не менеджери.

2. God object

Божественний об'єкт - антипаттерн, який описує зайву концентрацію занадто великої кількості різношерстих функцій, зберігання великої кількості різноманітних даних (об'єкт, навколо якого обертається додаток). Візьмемо невеликий приклад:
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