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);
}
Удобно, просто и компактно, не правда ли?
Показателем, хорошего разработчика является даже не частота использования паттернов, а скорее частота избеганий антипаттернов.
Незнание — худший враг, ведь своих врагов нужно знать в лицо.
Что ж, сегодня у меня на этом всё, всем спасибо))
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ