8. Golden hammer
The golden hammer is an anti-pattern that describes the belief in the complete universality of a solution and the application of this solution everywhere. Examples:-
A developer who once encounters a problem and finds a pattern for an ideal solution tries to stick this pattern everywhere, implementing it in current and all future projects, instead of looking for suitable solutions for specific cases.
-
A group of developers once created their own analogue of the cache for a specific situation (because none of the others fit), and as a result, already on the next project, in which there is no specific logic regarding the cache, they use it instead of working with ready-made libraries (for example , Ehcache). Because of this, a lot of bugs and inconsistencies come out, and as a result - a lot of time and nerves for nothing.
Everyone can face this antipattern. If you are a beginner, you may not have enough knowledge in design patterns and, as a result, you are trying to solve all problems in the only known way that you have mastered. If we are talking about professionals, they have a professional deformation. You have your own preferences in design patterns, and instead of using the right one, you use your favorite, relying on the logic that good work in the past guarantees the same result in the future.
The result of a mistake can be quite sad: from a bad, unstable, difficult to maintain implementation to a complete failure of the project. After all, there is no one pill for all diseases, just as there is no one template for all occasions.
9. Premature optimization
Premature optimization is an anti-pattern whose name speaks for itself.10 Spaghetti code
Spaghetti code is an anti-pattern that describes a piece of code that is poorly structured, confusing, and hard to understand, containing many jumps, such as: wrapping exceptions, conditions, loops. Previously, the main ally of this anti-pattern was the goto operator, now it is not actually used, which removes a number of complexities and problems associated with it.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;
}
}
}
}
}
Looks terrible, doesn't it? Unfortunately, this is the most common anti-pattern ((Even the author cannot parse such code in the future, while other developers, seeing it, think that if it works, then it’s okay, it’s better not to touch it. It often happens that initially it was a simple and very transparent method , but with the addition of new requirements, new and new conditions were gradually hung on it, which turned it into such a monster.If such a method appears, you need to refactor either it completely or some of the most confusing parts.As a rule, when developing a project, they allocate time for refactoring: for example , 30% of the sprint time for refactoring and tests.Well, if there is no hurry (although where without it.) Here is a good example of spaghetti code and its refactoring.
11 Magic numbers
A magic number is an anti-pattern that affects heterogeneous constants in a program without explaining their purpose or meaning. That is, as a rule, there is no adequate name or, in extreme cases, a comment explaining what and why. As well as spaghetti code, it is one of the most common anti-patterns. A person who is not the author of this code can hardly or cannot explain what it is and how it works (and the author himself will not be able to do it over time). As a result, when this number is changed or removed, the code magically stops working altogether. As an example, 36 and 73. As a fight against this anti-pattern, I advise review of code. It is necessary that your code be viewed by developers who are not involved in this section of the code, who do not have a blurred eye and questions will arise - what is it and why? And of course, you need to write more informative names or leave comments.12. Copy and paste programming
Programming by copy and paste is an anti-pattern, which means mindlessly copying someone else's code (copy and paste), as a result of which side effects may occur that we have not finished watching. Like copying and implementing methods with mathematical calculations or complex algorithms that we don't fully understand, all this may work in our case, but under any other circumstances it can lead to disaster. Let's say I needed a method to calculate the maximum number from an array. After digging around on the internet, I found this solution: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;
}
An array of numbers 3,6,1,4,2 comes to us, and as a result we get - 6. Great, let's leave it! But time passes, and an array 2,5, -7,2,3 comes to us, and the result we will have is -7. And this is no longer buzzing. And the whole point is thatMath.abs()
returns the maximum value of the absolute value, and ignorance of this leads to a crash, but only in a certain situation. Without understanding the solution in depth, you won't be able to test multiple cases. And such coding can also go beyond the internal construction, both stylistically and on a more fundamental, architectural layer. Such code will be more difficult to read and maintain. In addition, of course, we do not forget: one hundred percent copying of someone else's code is a special case of plagiarism. In cases where a programmer does not fully understand what he is doing and decides to take someone else's supposedly working solution, this is not only a minus for perseverance, but also actions to the detriment of the team, the project, and sometimes the whole company (so copy-paste carefully) .
13. Reinventing the wheel
The invention of the wheel is an anti-pattern, better known to us as the invention of the bicycle . In fact, this pattern is the opposite of the anti-pattern discussed above - copy-paste. Its essence lies in the fact that the developer implements his own solution for a problem for which solutions already exist, and many times better than the one invented by the programmer. Most often, this only leads to a loss of time and a decrease in the efficiency of the programmer: a solution can be found that is far from the best or not found at all. At the same time, it is impossible to discard the possibility of an independent solution, since this will lead to copy-paste programming in a direct way. The programmer must navigate the tasks that may be before him in order to competently solve them, using ready-made solutions or inventing his own. Very often, the reason for using this anti-pattern is a banal rush and, as a result, insufficiently deep analysis (search) of ready-made solutions. Invention of the unicycleis a case of the considered antipattern with a negative outcome. That is, the project needs a certain solution, and the developer creates it, but badly. At the same time, a good option already exists and is being successfully used. Bottom line: a huge waste of time. First, we create something non-working, and then we make efforts to refactor and replace it with something that already exists. As an example, the implementation of your own cache, when there are already a lot of existing ones. No matter how talented a programmer you are, you should remember that inventing a bicycle is at least a waste of time, and time, as you know, is the most valuable resource.14. Yo-yo problem
The yo yo problem is an anti-pattern in which the structure of an application is excessively fuzzy due to excessive fragmentation (for example, the inheritance chain is excessively broken). The “Yo-Yo problem” occurs when you need to understand a program whose inheritance hierarchy and nesting of method calls is very long and complex. The programmer therefore needs to navigate between many different classes and methods in order to control the behavior of the program. The term comes from the name of the yo-yo toy. As an example, let's consider the following chain: We have a technology interface:public interface Technology {
void turnOn();
}
The transport interface is inherited from it:
public interface Transport extends Technology {
boolean fillUp();
}
And then another ground transport interface:
public interface GroundTransportation extends Transport {
void startMove();
void brake();
}
And from it comes an abstract class of machines:
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();
}
Next is the Volkswagen abstract class:
public abstract class Volkswagen extends Car {
@Override
public void startMove() {
/*some realization*/
}
@Override
public void brake() {
/*some realization*/
}
}
And finally, the specific model:
public class VolkswagenAmarok extends Volkswagen {
@Override
public void fixCar(){
/*some realization*/
}
}
Here is a chain and makes you look for answers to questions like:
-
How many methods are in
VolkswagenAmarok
? -
What type should be inserted instead of the question mark for maximum abstraction:
? someObj = new VolkswagenAmarok(); someObj.brake();
15. Accidental complexity
Unnecessary complexity is the anti-pattern of introducing unnecessary complexity into a solution.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();
}
}
And accordingly enum
:
public enum ServiceType {
CAR(),
USER(),
FILE(),
PLAN(),
CUSTOMER()
}
It seems that everything is simple and good ... But what will happen to the rest of the methods? After all, they will all also be with a bunch switch
and a bunch of almost identical database queries, which in turn will greatly confuse and bloat our class. How could all this be made easier? Let's modernize ours a bit 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;
}
Now each type has the names of the original fields of its table. As a result, the description creation method turns into:
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);
}
Convenient, simple and compact, right? The indicator of a good developer is not even the frequency of using patterns, but rather the frequency of avoiding anti-patterns. Ignorance is the worst enemy, because you need to know your enemies by sight. Well, that's all for today, thank you all :)
GO TO FULL VERSION