JavaRush /Java Blog /Random EN /What are antipatterns? Parsing examples (part 2)

What are antipatterns? Parsing examples (part 2)

Published in the Random EN group
What are antipatterns? Exploring Examples (Part 1) What are antipatterns?  Parsing examples (part 2) - 1 Today we will continue our review of the most popular anti-patterns. If you missed the first part, it's here . So, design patterns are best practices, that is, time-tested examples of good practices for solving certain problems. In turn, antipatterns are their complete opposite, because they are patterns of traps and mistakes that are made when solving various problems (templates of evil). What are antipatterns?  Parsing examples (part 2) - 2Let's move on to the next development anti-pattern.

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:
  1. 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.

  2. 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.
“Programmers spend a huge amount of time thinking and worrying about non-critical places in the code, and trying to optimize them, which is extremely negative for subsequent debugging and support. We should forget about optimization altogether in, say, 97% of the cases; moreover, hasty optimization is the root of all evil. Conversely, we should give full attention to the remaining 3%,” Donald Knuth
What are antipatterns?  Parsing examples (part 2) - 3An example is adding indexes to a database prematurely. Why is it bad? And so, the indexes are stored as a binary tree, and accordingly, with each addition and removal of a new value, the tree will be recalculated, and this is all resources and time. Therefore, indexes should be added only when absolutely necessary (with a large amount of data and insufficient search speed on them) and on the most significant fields (which are most often searched for).

10 Spaghetti code

What are antipatterns?  Parsing examples (part 2) - 4Spaghetti 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. What are antipatterns?  Parsing examples (part 2) - 5Like 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. What are antipatterns?  Parsing examples (part 2) - 6Most 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. What are antipatterns?  Parsing examples (part 2) - 7

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:
  1. How many methods are in VolkswagenAmarok?

  2. What type should be inserted instead of the question mark for maximum abstraction:

    ? someObj = new VolkswagenAmarok();
           someObj.brake();
It is difficult to give a quick answer to such questions: you need to look and understand, and it is easy to get confused. But what if the hierarchy is much larger, longer and more complicated, has all sorts of overloads, redefinitions? And the fact that we will have a structure that is obscure due to excessive fragmentation. The best solution would be to reduce the redundant separation. In our case, leave Technology→Car→VolkswagenAmarok.

15. Accidental complexity

Unnecessary complexity is the anti-pattern of introducing unnecessary complexity into a solution.
"Any fool can write code that a computer can understand. Good programmers write code that a person can understand." - Martin Fowler
So what is complexity? It can be defined as the degree of difficulty with which the execution of each operation in the program is given. As a rule, complexity can be divided into two types. The first kind of complexity is the number of features that the system has. It can be reduced in only one way - to remove some function. You need to keep an eye on the written methods: if one of them is no longer used or is used and does not bring value, it must be removed. Moreover, you need to measure how all the methods in the application are used, in order to understand what is worth investing in (many places of reuse), and what can be abandoned. The second type of complexity - unnecessary complexity, is treated only by a professional approach. Instead of doing something "cool" (not only young developers have this disease), you need to think about how how to make it as simple as possible, because the best solution is always simple. For example, we have small adjacent labels with descriptions for some entities, as a user entity: What are antipatterns?  Parsing examples (part 2) - 8That is, we have the user id, the id of the language in which the description is being made, and the description itself. And we have auxiliary tables of the same type for the cars, files, plans, customers tables. Then how will inserting new values ​​into such tables look like for us?
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 switchand 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 :)
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION