Why create an interface for each DAO?

Asked

Viewed 1,215 times

13

I see a lot on the Internet the creation of an interface for each system DAO. I understand the importance of using interface mainly for an easy implementation exchange, I have even found an explanation saying if you use a database and exchange to another is just create new DAO’s and warn for dependency injection which are new that she should inject... Anyway, in times of ORM I do not see the need to change the whole DAO, depending on how it was programmed only a few adjustments in the BD exchange.

So if I have a system with only two tables User and Request (for example), what would be the advantage of me creating the user interfaces and Pedidodao to then create their implementations Usariodaoimlp and Pedidodaoimpl?

  • 3

    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.

2 answers

10


This depends more on "technology around" and plans for the future, and is not necessarily a general rule.

For example, what do I mean by "technology around"? If you are talking about Java, it was common for older technologies like J2EE until 1.4 (renamed Javaee from version 5 and very simplified) to require the creation of various interfaces and Factories. Or if not required, the amount of Boilerplate was such that the programmer ended up separating the parts anyway to reuse code and separate responsibilities.

There is a book just about "rethinking" certain practices that may have become obsolete over time in new versions of Java frameworks:

http://www.amazon.com/Real-World-Patterns-Rethinking-Practices/dp/0557078326

One of the reasons for creating interfaces is to enable remote method calls, something much more emphasized at the beginning of Javaee. If remote calls are in the plans, a separate interface for customers to call will be useful instead of tying them directly to an implementation class that should be in the same project.

Another reason for the interface is to separate and abstract the instantiation of DAO. Using a Factory, the DAO can be anything: a normal class, a proxy for a remote call, a reused object pool, etc. being accessed only by the interface, without touching the real object. This Javaee design pattern is less necessary in the presence of CDI: with @Inject you are free to decide later on what the instantiation of your class will be like, without writing Factories, and also without having to create a separate interface.

Note: I have no experience with Spring injection to compare with CDI, but well... nor am I sure if the question is about Java itself or if it is language independent.

And a third reason to have an interface is the one you mentioned in your question: normal separation between interface and implementation.

Now, if any of these situations apply to your case, then you have to think carefully (no easy rule replaces thinking!). In general I prefer to follow the YAGNI, and do not codify anything of use other than predictable visible. It is important to separate modules from your application, but you rarely need to make mincemeat of each class into 3 (interface, implementation and Factory).

Often future needs are so different than predicted that it is worth having something simpler. What happens more often? Change the entire database or add a new column or validation rule? Encode to make it easy to do what happens more frequently.

  • 3

    In chapter 6 of the book Linkei has a section called "Premature Encapsulation is the root of all evil", hahahahaha.

  • 2

    YAGNI is a little more strict than that. Instead of saying to "don’t code anything whose usefulness isn’t predictable," he says "don’t code anything you’re not needing right now".

  • Hehe, I should have said "visible" instead of "predictable" then ;-)

  • Marcus just to quote, in my case it’s java and I intend to use the EE specifications, but the idea was to be something independent of language. But with your answer I realized that language really matters in these cases because features like DI can make a difference in the choice of patterns and mode of programming. @Caffé thanks for the contributions that from the beginning in a summarized form gave a response similar to that of Marcus!

  • Another thing I remembered now that the programming approach changes a lot is who will use your code: you are making a lib/service for other people (better to have a good encapsulation from an early age) or a program just for yourself (If it’s not good, just go there in the code and refactor everything, ready!)

  • 2

    Just for clarification, until recently the Spring framework required the creation of interfaces to enable the creation of proxies in particular cases. For example, if you inject a session-scope bean, Spring actually injects a proxy that at runtime will delegate calls to the methods of the bean for the user session instance. The problem is that the Spring implementation (which uses Javassist) for dynamic proxies generation cannot generate the proxy if there is a constructor other than the default.

Show 1 more comment

6

The main advantage is the evolution and modularization of the system.

We have two possible approaches:

  1. 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.
  2. 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 DAOs 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.

  • 2

    In my opinion, the question referred to the reason for separating the DAO interface from the Daoimpl implementation, and not the reason for separating the Request User.

  • Victor, your answer was very complete from until I like to read! He spoke of concepts that I already had but went far beyond what I knew, I learned a little more from his reply! But it’s not my idea to make a super dao I’ve done it a lot and I know the headache it generates. I prefer to use composition for these cases rather than inheritance. But as @Marcus meant, my doubt was in the real need to keep making interfaces for all Daos, and maybe I missed quoting just this, that my Daos are not extensions of a genericDAO.

  • @Flavioandrade Beauty, all right.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.