8.Golden hammer
Золотий молоток - це антипаттерн, що описує впевненість у повній універсальності будь-якого рішення та застосування повсюдно. Приклади:-
Розробник, який одного разу стикається з якоюсь проблемою і знаходить патерн для ідеального рішення, намагається всунути цей шаблон скрізь, впроваджуючи в поточні та всі майбутні проекти, замість шукати відповідні рішення під конкретні випадки.
-
Група розробників одного разу створабо свій аналог кешу під конкретну ситуацію (бо жоден інший не підходив), і в результаті вже на наступному проекті, в якому немає специфічної логіки щодо кешу, вони використовують його замість того, щоб працювати з готовими бібліотеками (як наприклад , Ehcache). Через це вилазить купа багів і нестикування, і як результат - купа часу і нервів марно.
З цим антипаттерном можу зіткнутися все. Якщо ти новачок, можливо, тобі не вистачає знань у шаблонах проектування і, як наслідок, ти намагаєшся вирішити всі завдання єдиним відомим способом, який освоїв. Якщо ж ми говоримо про професіоналів, у них є професійна деформація. У тебе свої уподобання в шаблонах проектування, і замість використання потрібного використовуєш коханий, покладаючись на логіку, що хороша робота в минулому гарантує той самий результат і в майбутньому.
Підсумок помилки може бути дуже сумним: від поганої, нестабільної, важкої підтримки реалізації до повного провалу проекту. Адже немає жодної таблетки від усіх хвороб, як і немає одного шаблону на всі випадки життя.
9. Premature optimization
Передчасна оптимізація - це антипаттерн, назва якого каже за себе.10. Spaghetti code
Спагетті-код - це антипаттерн, що описує частину коду, яка є погано структурованою, заплутаною і важкою для розуміння, що містить багато всяких переходів, як-от: обертання винятків, умов, циклів. Раніше головним союзником цього антипаттерну був оператор goto, зараз його фактично не використовують, що прибирає низку складнощів та проблем, пов'язаних з ним.public boolean someDifficultMethod(List<String> XMLAttrList) {
...
int prefix = stringPool.getPrefixForQName(elementType);
int elementURI;
try {
if (prefix == -1) {
...
if (elementURI != -1) {
stringPool.setURIForQName(...);
}
} else {
...
if (elementURI == -1) {
...
}
}
} catch (Exception e) {
return false;
}
if (attrIndex != -1) {
int index = attrList.getFirstAttr(attrIndex);
while (index != -1) {
int attName = attrList.getAttrName(index);
if (!stringPool.equalNames(...)){
...
if (attPrefix != namespacesPrefix) {
if (attPrefix == -1) {
...
} else {
if (uri == -1) {
...
}
stringPool.setURIForQName(attName, uri);
...
}
if (elementDepth >= 0) {
...
}
elementDepth++;
if (elementDepth == fElementTypeStack.length) {
...
}
...
return contentSpecType == fCHILDRENSymbol;
}
}
}
}
}
Жахливо виглядає, чи не так? На жаль, це найпоширеніший антипаттерн(( Подібний код у майбутньому не може розібрати навіть автор, інші ж розробники бачачи це думають, якщо воно працює, то й добре, краще не чіпати. Часто буває, що спочатку це був простий і дуже прозорий метод) , але з додаванням нових вимог на нього поступово навішувалися нові та нові умови, що й перетворило його на такий монстра.Якщо з'являється такий метод, потрібно відрефакторити або його повністю або деякі найбільш заплутані частини.Як правило при розробці проекту виділяють час на рефакторинг: наприклад , 30% часу спринту на рефакторинг і тести.Ну це якщо без поспіху (хоча куди без неї).Ось тут є непоганий приклад спагетті коду та його рефакторингу.
11. Magic numbers
Магічне числа — це антипаттерн, який торкається різнорідних константів у програмі без пояснення їхньої мети, сенсу. Тобто зазвичай немає адекватного імені або на крайній випадок коментаря, який пояснює, що й навіщо. Також як і спагетті код, є одним із найпоширеніших антипаттернів. Людина, яка не є автором даного коду, важко може або зовсім не може пояснити, що це і як воно працює (та й сам автор згодом не зможе). У результаті зміні цього чи його видаленні код магічно перестає працювати зовсім. Як приклад, 36 та 73. Як боротьба з цим антипаттерном раджу review of code. Потрібно, щоб ваш код переглядали розробники, які не задіяні в даній ділянці коду, у яких не замилене око і виникатимуть питання — а що це і навіщо? Ну і звичайно, потрібно писати більш інформативні імена чи залишати коментарі.12. Copy and paste programming
Програмування шляхом копіювання та вставки - це антипаттерн, що передбачає бездумне копіювання чужого коду (copy and paste), внаслідок чого можуть виникати побічні ефекти, які ми не доглянули. Як, наприклад, копіювання та впровадження методів з математичними обчисленнями чи складними алгоритмами, які ми до кінця не розуміємо, все це може працювати в нашому випадку, але за будь-яких інших обставин може призвести до біди. Припустимо, мені потрібний був метод для обчислення максимального числа з масиву. Покопавшись в інтернеті, я знаходжу це рішення:public static int max(int[] array) {
int max = 0;
for(int i = 0; i < array.length; i++) {
if (Math.abs(array[i]) > max){
max = array[i];
}
}
return max;
}
До нас приходить масив чисел 3,6,1,4,2, і як результат нам приходить - 6. Добре, залишаємо! Але минає час, і до нас приходить масив 2,5-7,2,3, і результат у нас буде -7. А це вже не гуд. А вся справа в тому, щоMath.abs()
повертає максимальне значення абсолютної величини, і незнання цього призводить до краху, але у певній ситуації. Без розуміння рішення углиб, ви не зможете перевірити кілька випадків. А ще таке кодування може виходити за рамки внутрішньої побудови, як стилістично, так і на фундаментальнішому, архітектурному шарі. Такий код буде важче вичитувати та підтримувати. Крім того, звичайно, не забуваємо: стовідсоткове копіювання чужого коду – це окремий випадок плагіату. У тих випадках, коли програміст не до кінця розуміє те, що він робить, і вирішує взяти чуже нібито робоче рішення - це не тільки мінус до посидючості, а й дії на шкоду команди, проекту та й іноді всієї компанії (так що копіпастим обережно) .
13. Reinventing the wheel
Винахід колеса - це антипаттерн, більш відомий у нас як винахід велосипеда . По суті, цей шаблон є протилежністю розглянутому вище антипаттерну — копіпаст. Суть його полягає в тому, що розробник реалізує власне рішення для завдання, для якого вже існують рішення, причому у рази краще, ніж придумане програмістом. Найчастіше це призводить лише до втрати часу та зниження ефективності роботи програміста: рішення може бути знайдено далеко не найкраще чи взагалі не знайдено. При цьому відкидати можливість самостійного рішення не можна, тому що це пряма дорога призведе до програмування копіпастом. Програміст повинен орієнтуватися у завданнях, які можуть постати перед ним, щоб грамотно їх вирішити, використовуючи готові рішення чи винаходячи власні. Дуже часто причиною використання цього антипаттерну є банальний поспіх і як результат недостатньо глибокий аналіз (пошук) готових рішень. Винахід одноколісного велосипеда- Це випадок аналізованого антипаттерну з негативним результатом. Тобто для проекту необхідне певне рішення і розробник створює його, але погано. У цей час хороший варіант вже існує і успішно використовується. Підсумок: втрата величезної кількості часу. Спершу ми створюємо щось неробоче, а потім докладаємо зусиль для рефакторингу та заміни на щось уже існуюче. Як приклад — реалізація власного кешу, коли вже багато існуючих. Яким би талановитим ви були програмістом, слід пам'ятати, що винахід велосипеда — це як мінімум витрата часу, а час, як відомо, — найцінніший ресурс.14. Yo-yo problem
Проблема йо йо — антипаттерн, при якому структура застосування надмірно розмита у зв'язку з надмірною фрагментацією (наприклад, надмірно розбитий ланцюжок успадкування). "Проблема Йо-Йо" виникає, коли необхідно розібратися в програмі, ієрархія успадкування та вкладеність викликів методів якої дуже довга і складна. Програмісту внаслідок цього необхідно лавірувати між безліччю різних класів та методів, щоб контролювати поведінку програми. Термін походить від назви іграшки йо-йо. Як приклад, давайте розглянемо такий ланцюжок: У нас є інтерфейс технологій:public interface Technology {
void turnOn();
}
Від нього успадковується інтерфейс транспорту:
public interface Transport extends Technology {
boolean fillUp();
}
А далі ще інтерфейс наземного транспорту:
public interface GroundTransportation extends Transport {
void startMove();
void brake();
}
А від нього йде абстрактний клас машин:
public abstract class Car implements GroundTransportation {
@Override
public boolean fillUp() {
/*some realization*/
return true;
}
@Override
public void turnOn() {
/*some realization*/
}
public boolean openTheDoor() {
/*some realization*/
return true;
}
public abstract void fixCar();
}
Далі - абстрактний клас фольксвагена:
public abstract class Volkswagen extends Car {
@Override
public void startMove() {
/*some realization*/
}
@Override
public void brake() {
/*some realization*/
}
}
І нарешті, конкретна модель:
public class VolkswagenAmarok extends Volkswagen {
@Override
public void fixCar(){
/*some realization*/
}
}
Ось такий ланцюжок і змушує шукати відповіді на запитання на кшталт:
-
Скільки методів у
VolkswagenAmarok
? -
Який тип потрібно вставити замість знака питання максимальної абстракції:
? someObj = new VolkswagenAmarok(); someObj.brake();
15. Accidental complexity
Непотрібна складність - це антипаттерн внесення непотрібної складності у рішення.public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description)throws Exception {
switch (type){
case CAR:
jdbcTemplate.update(CREATE_RELATION_WITH_CAR,languageId, serviceId, description);
case USER:
jdbcTemplate.update(CREATE_RELATION_WITH_USER,languageId, serviceId, description);
case FILE:
jdbcTemplate.update(CREATE_RELATION_WITH_FILE,languageId, serviceId, description);
case PLAN:
jdbcTemplate.update(CREATE_RELATION_WITH_PLAN,languageId, serviceId, description);
case CUSTOMER:
jdbcTemplate.update(CREATE_RELATION_WITH_CUSTOMER,languageId, serviceId, description);
default:
throw new Exception();
}
}
І відповідно enum
:
public enum ServiceType {
CAR(),
USER(),
FILE(),
PLAN(),
CUSTOMER()
}
Начебто все просто і добре ... Але що буде з іншими методами? Адже вони теж будуть з купою switch
і купою майже однакових запитів до бази даних, що в свою чергу сильно заплутає і роздмухує наш клас. Як це можна було б зробити простіше? Давайте трохи модернізуємо наш enum
:
@Getter
@AllArgsConstructor
public enum ServiceType {
CAR("cars_descriptions", "car_id"),
USER("users_descriptions", "user_id"),
FILE("files_descriptions", "file_id"),
PLAN("plans_descriptions", "plan_id"),
CUSTOMER("customers_descriptions", "customer_id");
private String tableName;
private String columnName;
}
Тепер кожен тип має назви оригінальних полів його таблиці. У результаті метод створення опису перетворюється на:
private static final String CREATE_RELATION_WITH_SERVICE = "INSERT INTO %s(language_id, %s, description) VALUES (?, ?, ?)";
public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description) {
jdbcTemplate.update(String.format(CREATE_RELATION_WITH_SERVICE, type.getTableName(), type.getColumnName()),languageId, serviceId, description);
}
Зручно, просто і компактно, чи не так? Показником хорошого розробника є навіть не частота використання патернів, а скоріше частота уникнень антипатернів. Незнання - найгірший ворог, адже своїх ворогів треба знати в обличчя. Що ж, сьогодні у мене на цьому все, дякую всім)))
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ