Let’s get more technical. ‘Know your Language’ is a new category in my blog, about Programming Languages and their most common problems in enterprise environments
It is adopted by major companies and there are a lot of job openings for it. Also, you can always find some tool or framework that solves that particular problem you are facing, given it’s huge history and fame.
It is also extremely verbose, prone to code bloat, low level (compared with newer languages), clunky all around and resource-eating.
I know that all boils down to “there are bad programs in all languages”, but I believe that there are design details of each language that puts developers in a easy position to fall down in some traps, specially considering that we usually tend to “follow the guidelines” of the gurus.
Sticking to code and design, let’s see what are the most common pitfalls I’ve seen in Java projects.
Pretty simple. You do your clone, access the directory and do a build command
And then it fails.
Usually this means an environment dependency or a manually added hosts to run the build or tests. Unless your build actually runs in a single command, you are doing it wrong.
Gradle is a specially offender in this case because of the easiness to create customized tasks. Avoid overly romantic tasks and approaches, like code generation.
Thrown Design Patterns
The idea behind a design pattern is somewhat simple: You have a recurrent problem and a design pattern is a known way to solve that problem. Pretty straightforward, Problem leads to Solution. The issues arise when you like one solution and tends to apply it anywhere you see fit (the old “when all you have is a hammer, everything looks like a nail”).
Simple example, a Strategy Pattern. In cool words Strategy is a way that enables algorithm selecting at runtime. In common terms, it is used to hide an “if” statement.
Interface with price calculation
We could have our customer class:
And now we can use it neatly:
Cool right? How can it be bad? Let’s apply this everywhere!
But there is a catch, even in this simple example. How do I know when to use HappyHourStrategy or NormalStrategy? Can I hide this specific if with another strategy? If yes, would it be better than a single if? Maybe, instead of this, I could add in the customer class:
Is it so bad? You’re making the decision in a single IF-ELSE instead of building an indirection layer, and besides it, still in a single point, in case you need to refactor or read it. If this if else grow in number, maybe it is time to refactor, but right now, I think it is good enough.
Design patterns are tools, not the only way to do something.
Functional Interface Misuse
Functional interfaces are awesome. It can really make your code more elegant and simple. Let’s take a look, using OReilly as a resource.
In the old days, we would use it like this:
With Java8 and Lambdas, the compiler knows that you are calling the single method from the functional interface.
Neat, right? The “()” syntax is useful because you can just pass the arguments there and the compiler knows the types.
The problems arise when you try to build a DSL for fluent language with this, because nowadays you see things like:
And to build this, you create a single functional interface for each of the dots, like this:
And in maClass.needDoThis() you get:
I’ve seen it more than once. Don’t do this. You created a lot of complication just to make a pretty oneliner, which, in the end, is just a single method anyway. Wouldn’t be easier if you just called the method?
Poor (or overused) OO
I know that this is possible in any OO language, but Java suffers it hard. There’s a lot of confusion about concepts like composition over inheritance, or loose coupled parts or plain simple class design, and big problems arise from this confusion.
(Java8 default methods in interfaces made this even more common)
A great smell of bad class design is the
instanceof operator used a lot. It, of course, has its uses, but always think through when you use it to check if your design is OO adherent.
A bad example from StackOverflow:
You could replace and put the responsibility inside the Animal interface:
(I know, it’s just an academic example, but you got the idea)
In the other side of the spectrum, there’s the overly complex class design, which should also be avoided.
Reflection is one of the most powerful features of Java. You can, basically, access anything at runtime, even modify the methods or fields. The majority of frameworks use it to be able to call your code using the conventions of the language (getters and setters. How do you think hibernate fills your fields?).
Again, the problem with it is the layer of abstraction that you put in your code. When you use reflection, your errors will be in runtime, and your IDE will not be able to help you with the “find usages”. You are basically on your own.
As a rule of thumb, use Reflection to build frameworks, because they need to be able to call yet nonexistent code. To build your applications, avoid it always.
I think that this one is the most “personal” problem, because I see a lot of people praising AOP and the benefits of AspectJ, but I find myself enjoying it less and less as time passes. Right now I wouldn’t use it in application code, but, as it is with reflection, it is useful for framework creation.
One of the main reasons why I think it is harmful is for debugging. You came across a legacy system with a bug and you catch a glimpse of the log:
2017-05-25 00:22:31.496 INFO 1 --- [Thread-3] maProject.maPackage.maClass1 : Trying to do something 2017-05-25 00:22:31.512 INFO 1 --- [Thread-2] maProject.maPackage.maClass2 : I shoudn't be trying this, but I am nonetheless 2017-05-25 00:22:31.530 INFO 1 --- [Thread-3] maProject.maPackage.maClass3 : Continuing ok, nothing to see here
Cool, maClass2 seems to be the aggressor. Then you open the class and don’t find the log line. And don’t see where maClass1 is calling maClass2. And neither the call to maClass1. Everything is build around aspects, and your whole project is a huge collection of indirection layers, one after another.
Avoid it. It is not harmful to see a
log.info("") inside your method.
Sometimes it is better to be straightforward than clever.
Just don’t. I mean it.