Всім доброго доби! Днями я проходив співбесіду, і мені поставабо питання про антипаттерни: що це за звір такий, які бувають їхні види та приклади на практиці. На запитання я, звичайно, відповів, але дуже поверхово, тому що не сильно заглиблювався у вивчення цього питання. Після співбесіди я почав нишпорити по просторах інтернету, дедалі більше занурюючись у цю тему. Сьогодні я хотів би зробити невеликий огляд найпопулярніших антипаттернів та їх прикладів, прочитання якого, можливо, дасть вам необхідні знання у цьому питанні. Почнемо! Отже, як міркувати, що таке антипаттерн, давайте згадаємо, що таке патерн. Паттерн- Це повторна архітектурна конструкція для вирішення часто зустрічаються проблем або ситуацій, що виникають при проектуванні програми. Але сьогодні у нас йдеться не про них, а про їхні протилежності — антипатерни. Антипаттерн - це поширений підхід до вирішення класу проблем, що часто зустрічаються, який є неефективним, ризикованим або непродуктивним. Інакше висловлюючись, це патерн помилок (також іноді званий пасткою). Як правило, антипатерни ділять на такі види:
Architectural antipatterns - антипатерни архітектури, що виникають при проектуванні структури системи (як правило архітектором).
Management Anti Pattern - антипатерни в галузі управління, з якими зазвичай стикаються різноманітні менеджери (або групи менеджерів).
Development Anti Pattern - антипатерні проблеми розробки, що виникають під час написання системи рядовими програмістами.
Екзотика антипатернів набагато ширша, але ми їх розглядати сьогодні не будемо, тому що для звичайних розробників цього буде з головою. Спочатку як приклад розглянемо антипаттерн у сфері управління.
1. Analytical paralysis
Аналітичний параліч- Вважається класичним організаційним антипаттерном. Його суть полягає в надмірному аналізуванні ситуації при плануванні, так що рішення чи дія не робляться, по суті, паралізуючи розробку. Найчастіше це трапляється у тих випадках, коли мета полягає у досягненні досконалості та повної завершеності періоду аналізу. Цей антипаттерн характеризується ходінням по колу (такий собі замкнутий цикл), переглядом та створенням детальних моделей, що у свою чергу заважає робочому процесу. Наприклад, ви намагаєтеся передбачити речі рівня: а якщо раптом користувач захоче створити список співробітників на основі четвертих і п'ятих літер їх імені, з включенням до списку проектів, яким вони приділабо найбільше робочих годин між Новим Роком і Восьмим березня за чотири попередні роки ? Насправді це надлишок аналізу.привів Kodak до банкрутства . Ось пара невеликих порад для боротьби аналітичним паралічем:
Потрібно визначити довгострокову мету як маяк для прийняття рішень, щоб кожне ваше рішення наближало до мети, а не змушувало тупцювати на місці.
Не концентруватися на дрібницях (навіщо приймати рішення щодо незначного нюансу так, ніби воно останнє у житті?)
Вкажіть крайній термін для прийняття рішення.
Не намагайтеся зробити завдання досконало: краще зробити дуже добре.
Занадто поглиблюватись і розглядати інші управлінські антипатерни ми зараз не будемо. Тому без передмов переходимо до деяких архітектурних антипаттернів, адже, швидше за все, цю статтю читають майбутні розробники, а не менеджери.
2. God object
Божественний об'єкт - антипаттерн, який описує зайву концентрацію занадто великої кількості різношерстих функцій, зберігання великої кількості різноманітних даних (об'єкт, навколо якого обертається додаток). Візьмемо невеликий приклад:
publicclassSomeUserGodObject{privatestaticfinalString FIND_ALL_USERS_EN = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users;privatestaticfinalString FIND_BY_ID ="SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users WHERE id = ?";privatestaticfinalString 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 = ?)";privatestaticfinalString FIND_BY_EMAIL ="SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_dateFROM users WHERE email = ?";privatestaticfinalString LIMIT_OFFSET =" LIMIT ? OFFSET ?";privatestaticfinalString 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";privatestaticfinalString CREATE_USER_EN ="INSERT INTO users(id, phone, email, first_name_en, middle_name_en, last_name_en, created_date) "+"VALUES (?, ?, ?, ?, ?, ?, ?)";privatestaticfinalString FIND_ID_BY_LANG_CODE ="SELECT id FROM languages WHERE lang_code = ?";........privatefinalJdbcTemplate jdbcTemplate;privateMap<String,String> firstName;privateMap<String,String> middleName;privateMap<String,String> lastName;privateList<Long> permission;........@OverridepublicList<User>findAllEnCustomers(Long permissionId){return jdbcTemplate.query( FIND_ALL_CUSTOMERS + ORDER,userRowMapper(), permissionId);}@OverridepublicList<User>findAllEn(){return jdbcTemplate.query(FIND_ALL_USERS_EN + ORDER,userRowMapper());}@OverridepublicOptional<List<User>>findAllEnByEmail(String email){var query = FIND_ALL_USERS_EN + FIND_BY_EMAIL + ORDER;returnOptional.ofNullable(jdbcTemplate.query(query,userRowMapper(), email));}.............privateList<User>findAllWithoutPageEn(Long permissionId,Type type){switch(type){case USERS:returnfindAllEnUsers(permissionId);case CUSTOMERS:returnfindAllEnCustomers(permissionId);default:returnfindAllEn();}}..............…
privateRowMapper<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із бізнес-логікою. Такий божественний об'єкт стає величезним та неповоротким для адекватної підтримки. Нам доводиться возитися з ним у кожному шматочку коду: багато вузлів системи покладаються на нього і жорстко з ним пов'язані. Підтримувати такий код стає складніше і складніше. У разі його треба розрубати деякі класи, в кожного з яких буде лише одне призначення (мета). У цьому прикладі можна розбити на клас дао:
publicclassUserDaoImpl{privatestaticfinalString FIND_ALL_USERS_EN = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users;privatestaticfinalString FIND_BY_ID ="SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users WHERE id = ?";........privatefinalJdbcTemplate jdbcTemplate;........@OverridepublicList<User>findAllEnCustomers(Long permissionId){return jdbcTemplate.query(FIND_ALL_CUSTOMERS + ORDER,userRowMapper(), permissionId);}@OverridepublicList<User>findAllEn(){return jdbcTemplate.query(FIND_ALL_USERS_EN + ORDER,userRowMapper());}........}
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ