Advice wanted on a complex structure in java (DAO

2020-07-11 04:39发布

问题:

Introduction

I am trying to make a rather complex structure in Java with interfaces, abstract classes and generics. Having no experience with generics and only average experience with creating good OOP designs, this is beginning to prove quite a challenge.

I have some feeling that what I'm trying to do cannot actually be done, but that I could come close enough to it. I'll try to explain it as brief as I can. I'm just going to tell straight away that this structure will represent my DAO and service layers to access the database. Making this question more abstract would only make it more difficult.

My DAO layer is completely fine as it is. There is a generic DAO interface and for each entity, there is a DAO interface that extends the generic one and fills in the generic types. Then there's an abstract class that is extended by each DAO implementation, which in turn implement the corresponding interface. Confusing read for most probably, so here's the diagram showing the DAO for Products as an example:

Now for the service classes, I had a similar construction in mind. Most of the methods in a service class map to the DAO methods anyway. If you replace every "DAO" in the diagram above with "Service", you get the basis for my service layer. But there is one thing that I want to do, based on the following idea I have:

Every service class for an entity will at least access one DAO object, namely the DAO of the entity that it is designed for.

Which is...

The question/problem

If I could make a proper OO design to make each service class have one instance variable for the DAO object of their respective entity my service layer would be perfect, in my view. Advice on this is welcome, in case my design is not so good as it seemed.

I have implemented it like this:

Class AbstractService

public abstract class AbstractService<EntityDAO> {

    EntityDAO entityDAO;

    public AbstractService() {
        entityDAO = makeEntityDAO(); //compiler/IDE warning: overridable method call in constructor
    }

    abstract EntityDAO makeEntityDAO();
}

Class ProductServiceImpl

public class ProductServiceImpl extends AbstractService<ProductDAOImpl> {

    public ProductServiceImpl() {
        super();
    }

    @Override
    ProductDAOImpl makeEntityDAO() {
        return new ProductDAOImpl();
    }
}

The problem with this design is a compiler warning I don't like: it has an overridable method call in the constructor (see the comment). Now it is designed to be overridable, in fact I enforce it to make sure that each service class has a reference to the corresponding DAO. Is this the best thing I can do?

I have done my absolute best to include everything you might need and only what you need for this question. All I have to say now is, comments are welcome and extensive answers even more, thanks for taking your time to read.

Additional resources on StackOverflow

Understanding Service and DAO layers

DAO and Service layers (JPA/Hibernate + Spring)

回答1:

Just a little note first: usually in an application organized in layers like Presentation / Service / DAO for example, you have the following rules:

  • Each layer knows only the layer immediately below.
  • It knows it only by it's interfaces, and not by it's implementation class.

This will provide easier testing, a better code encapsulation, and a sharper definition of the different layers (through interfaces that are easily identified as public API)

That said, there is a very common way to handle that kind of situation in a way that allow the most flexibility: dependency injection. And Spring is the industry standard implementation of dependency injection (and of a lot of other things)

The idea (in short) is that your service will know that it needs a IEntityDAO, and that someone will inject in it and implementation of the interface before actually using the service. That someone is called an IOC container (Inversion of Control container). It can be Spring, and what it does is usually described by an application configuration file and will be done at application startup.

Important Note: The concept is brilliant and powerful but dead simple stupid. You can also use the Inversion of Control architectural pattern without a framework with a very simple implementation consisting in a large static method "assembling" your application parts. But in an industrial context it's better to have a framework which will allow to inject other things like database connection, web service stub clients, JMS queues, etc...

Benefits:

  • Your have an easy time mocking and testing, as the only thing a class depends on is interfaces
  • You have a single file of a small set of XML files that describe the whole structure of your application, which is really handy when your application grows.
  • It's a very widely adopted standard and well - known by many java developers.

Sample java code:

public abstract class AbstractService<IEntityDAO> {

    private IEntityDAO entityDAO; // you don't know the concrete implementation, maybe it's a mock for testing purpose

    public AbstractService() {
    }

    protected EntityDAO getEntityDAO() { // only subclasses need this method
    }

    public void setEntityDAO(IEntityDAO dao) { // IOC container will call this method
        this.entityDAO = dao;
    }
}

And in spring configuration file, you will have something like that:

<bean id="ProductDAO" class="com.company.dao.ProductDAO" />

[...]

<bean id="ProductService" class="com.company.service.ProductService">
    <property name="entityDAO" ref="ProductDAO"/>
</bean>