The main advantage is the evolution and modularization of the system.
We have two possible approaches:
- Create 20 interfaces
DAO
with 5 or 6 methods each, you have interfaces UsuarioDAO
, PedidoDAO
, ContaDAO
, ClienteDAO
... When you create the seller module, you will create a VendedorDAO
, a few methods and a VendedorDAOImpl
.
- Create a single gigantic interface
MegaDAO
with a 100 methods there. When you go to create the seller module, you will add a few more methods there and implement them in the MegaDAOImpl
.
Now let’s analyze the advantages and disadvantages of each alternative:
Note that in approach 2, your MegaDAO
is not a stable interface in time as it is always changing. Implementations of this interface (MegaDAOImpl
) are also not stable in time, as they are always changing. Whenever you create or modify some module so that the access to the database changes, the MegaDAO
and the MegaDAOImpl
will change.
Approach 2 also makes modularization difficult, as it creates an interface from which all modules of the system depend, and effectively ties all parts of the system to each other in a single monolithic block, even if the different parties have little or no relationship with each other. If you then want to separate the program into two, one for the accounting sector and the other for the maintenance sector, your MegaDAO
and all its implementations (even if it is only one) will give you a headache, because it puts all the system’s functionalities together, including those that are expendable for the scenario in question. The result of this is the famous bloat software.
In addition, let’s assume that the invoice query module has a peculiarity: It uses two distinct databases for any special reason. In approach 1, this is a detail of ConsultaNotaFiscalDAO
and its (possibly multiple) implementations. If you have a single MegaDAO
, will need to solve this with a gambiarra or with a complete Refactoring, as there will be a lot of methods where the use of two databases does not make any sense, and with that there is the risk (may or may not happen) of the gambiarra (or the need for complete re-factoring) end up cascading a series of changes in other parts of the system that should not be undergoing change.
Usually the implementation of most DAO
s will look like this: It goes into the ORM and queries, inserts, changes and/or deletes records. However "normally" and "most" is not the same thing as "always" and "all". If there is an exception, say, the PrazoDeEntregaDAO
that instead of going in the relational database, goes in an external service, it will bring problems in approach 2 and will complicate your MegaDAOImpl
.
Perhaps you argue that in this case the PrazoDeEntregaDAO
shouldn’t even be a DAO
. But let’s assume that originally he was a DAO
and over time it has evolved into this special behavior. In approach 1, all you will need to do is change the implementation of the DAO
for a special implementation other than normal. In approach 2, you will be screwed.
Let’s assume you’re sketching out what the implementation of ChatDAO
. For this you have created a quick mock that saves the information only in memory, the ChatDAOMockImpl
. You also use this implementation to do unit testing. This is simple and natural in approach 1. Already in approach 2, you get screwed again.
In approach 2, as your system grows, the MegaDAO
and the MegaDAOImpl
also grow. Imagine that after 5 years, you’ve added more than 40 distinct modules and have a large system with multiple developers working. And there’s also a MegaDAOImpl
with about twenty thousand lines of code that your IDE even has difficulty opening and developers constantly complaining about merge problems with git or svn in this class.
Anyway, the problem with yours DAO
unique is that you violate various principles of object-oriented programming:
- The implementation of its
DAO
single will be a God class, which consists of one of the worst anti-standards object-oriented programming.
- Your system will have a high degree of coupling, for his
DAO
(both interface and implementation) tie everything to everything.
- Your system will have a low degree of cohesion, because a large amount of functionalities unrelated to each other or unrelated to each other will be coexisting in the same class.
- You’ll be violating the open-closed principle, where functionalities must be extendable and reusable without the need for existing code modification. With a
DAO
single, any attempt to extend the functionalities entail a code change.
- You’ll be violating the principle of single responsibility, because its interface
DAO
(as well as the implementation) will have a lot of different responsibilities, and as a result it will be difficult to be used.
- You will be violating, quite directly, the principle of segregation of interfaces, which says that several specific purpose interfaces are better than a single large generic purpose interface.
The only advantage of approach 2 is to have all the code in one place and simplify a little the methods of connection management with the database, generic queries and things like that. And that’s it. This advantage is easily defeated by the large amount of disadvantages.
You might argue, ah but I only have two modules, User and Request, do I really need to break up? The answer is that the whole system that becomes large starts small, so the sooner they are separated, the easier will be the evolution of the system.
If you do not realize the need for an interface, it is because you should not create it. Create only the interfaces you’ve noticed that are already needed right now. Creating an interface in case one day needs to replace the implementation is a bad reason to create it. We should only now write the code that is needed now, avoiding waste and unnecessary increase of complexity.
– Caffé