7

I’ve finished Effective Java book and after it I got confused. In this book Bloch insist on decreasing mutability, making fields final, refuse public constructors and favor factories or builder pattern, disabling inheritance using final classes and methods and etc.

But right now I’m working on a project based on Spring framework and:

  1. All entities must have default constructor (Spring Data JPA)
  2. All services should be non-final to mock them
  3. All classes must be non-final to have a chance to be advised (Spring AOP, AspectJ)

At the first blush seems that most part of the book is violated by AOP, mocking and by Spring Data/Hibernate.

How should I deal with it? Is the book useless in practice?

Pasha
  • 1,768
  • 6
  • 22
  • 43
  • 4
    For example, the book says: *Classes should be immutable unless there’s a very good reason to make them mutable* - that doesn't mean you should make every class immutable, it depends on many factors. Also, with regard to `entities` - this is a [standard](https://stackoverflow.com/questions/3295496/what-is-a-javabean-exactly) - that doesn't mean every business object should follow this standard, it depends... – Oleksandr Pyrohov May 20 '18 at 12:12
  • If u don’t provide default constructor then an exception is thrown. So it’s not a standard, that I can follow or not – Pasha May 20 '18 at 12:14
  • And, this is a situation when you should provide a default constructor. But, in addition, you could have a plenty amount of classes that don't act like an `entities`, and may or may not include a default constructor - think about applying the advises from the book to these classes. – Oleksandr Pyrohov May 20 '18 at 13:03

4 Answers4

7

Effective Java describes general best practices that better be followed when it's possible. But it considers pure java, not any framework features.

Framework defines the architecture of the project and you should follow these rules. The framework has it's own best practices.

Immutable objects are good because they are inherently thread-safe. Their invariants are established by the constructor, and if their state cannot be changed, these invariants always hold. But there is no strict rule that every object should be immutable, sometimes it is not possible in scope of the given task.

Builders and factory patterns are still good and could be used in scope of a Spring project. I used both Spring dependencies and Factory patterns in real project, because the Factory still allows you to @Autowire objects.

As an overall example, I used Spark functions in Spring project. Some of the functions were @Autowire Spring services. But the functions themselves were not Spring services. You cannot create them by new Function() because then Spring will be not able to autowire the Service. But with Factory, you can help Spring to do so.

There are many good design principles like SOLID, DRY, KISS, design patterns which are usually helpful and allow you organize the code much better. But sometimes, in real life, you simple just cannot apply all of them to your particular case. The main rule here is that you should not absolutize any of best practices and find a middle ground between achievement of final goal and applying the best practices.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
J-Alex
  • 6,881
  • 10
  • 46
  • 64
2

The are several dimensions to consider here:

  • Sometimes best practices coming from different sources simply don't fit together nicely. But it is always a good best practice ... to follow best practices (to not surprise your readers). Meaning: when Spring comes with all these "guidelines", then you follow them. Even when that means violating "more generic" best practices.
  • Also keep in mind that technical restrictions often affect what you as a developer can do. As in: Java is as it is. So, when a framework wants to create + fill objects using a default constructor, and then reflection ... well, that is like the only choice you have in Java. Other languages might enable you to do such things in a more concise way, but in Java, as said: your options are simply limited.

Thus, the best approach is: regard "best practices" like multiple circles drawn around the same central point. You first focus on those that line up directly with the technology you are using (say Spring). Then you can check "what else is there", and you try to follow these ideas. But something that is emphasized by the innermost circle always trumpets things derived from "further outside".

GhostCat
  • 137,827
  • 25
  • 176
  • 248
2

Design is a means to an end, design patterns are standard solutions to common problems.

Design books should not be read as "always to do this", but as "this is a standard solution for this problem". If you don't have the problem, you don't need to solve it. And sometimes your particular context may permit an easier or otherwise better solution.

So let's see why Joshua Bloch recommends these items, shall we? (Sorry for the occasional inaccuracy, I'm paraphrasing from memory here)

decreasing mutability, making fields final

Immutable state is referentially transparent and therefore easier to reason about. It is also inherently thread-safe.

... but databases hold mutable data. A database application must therefore deal with mutable data, and the clearest way to express this is using mutable objects.

To help with concurrent mutation, databases shield the application from concurrent changes by means of transactions.

That is, immutable objects, and transaction scoped objects are different solutions for the problem of reasoning about concurrent mutations.

refuse default constructors

When working with objects, we generally want them to be fully initialized.

We can enforce this by writing a constructor that initializes the object.

... but state persisted in a database has already been initialized. By providing a default constructor, the framework can recreate the object while bypassing initialization.

That is, we can ensure that objects are initialized by initializing them in a constructor, or by having a framework recreate initialized objects.

(BTW, complex JPA entities often use both approaches: they have a public constructor for initializing, and a package-visible constructor for recreating)

disabling inheritance

When you are writing a library, you need to be able to change your code without breaking clients, and you may even need to prevent malicious clients from breaking your code. Inheritance can interfere with both unless carefully managed.

When you are writing an application, your code is not usually subclassed by other teams, making it easy to change sub classes when changing the super class.

That is, you can prevent super class changes from breaking sub classes by preventing subclassing, by restricting yourself to superclass changes that can not break subclasses, or by changing subclasses when they break.

How should I deal with it? Is the book useless in practice?

By considering these standard solutions to common problems, when you encounter these problems.

meriton
  • 68,356
  • 14
  • 108
  • 175
0

This is a good concern but according to me not all "best practices" can be followed at a go in the same project actually what you need to do is to try to follow them anywhere that its possible but when there is no option I think you should first consider achieving what your project was meant to archieve.

Actually I have been following the best practices for android from the google team but since iO2018 event most of the previous best practices were termed as "bad practice" but this does not mean that the speakers were useless its only time and conditions that have changed.

So my advice would be that you follow the best practices that are applicable to your current project and ignore those that limits you from archieving your desired goal

Austine Gwa
  • 804
  • 1
  • 9
  • 18
  • You should read this: http://www.satisfice.com/blog/archives/27. The phrase "best practice" should probably be **dropped** from the IT vocabulary. And your answer illustrates this. In fact, these things that people were (or are) claiming to be best practice are (literally) not. – Stephen C May 20 '18 at 14:16
  • But how to spot excessive ones? – Pasha Jun 03 '18 at 22:35