Basically, the two questions linked by @bfavaretto would already answer your question regarding class modeling. These are:
However, you can specifically analyze your case from the point of view of an Application Use Case or the EJB architecture.
Use Cases
Use Cases of an application may inherit other Use Cases . This includes running certain given business rules of the legacy use case. This is common in system modeling.
However, inheritance in Use Cases does not function exactly like inheritance in object orientation. It consists of inserting additional steps into the main Use Case.
In practice, this does not mean that you necessarily need to use inheritance in the classes that implement these use cases. There are several techniques for creating extension points in your code.
Enterprise Java Beans
I would not recommend using inheritance in EJB classes. These classes have a working agreement with container . With inheritance you can end up in complicated situations in which it is difficult to know if any details should or should not be inherited. This is not to mention possible issues with annotations, transactions and visibility of access to the elements.
Delegation
My suggestion is that the inheritance of business rule classes be avoided to the maximum. Using delegation allows you to reuse the business rules where you need it without the need for gambiarras with class hierarchy.
Avoid coupling
Preferably, make your business rules methods have low coupling to be reused.
For example, instead of a method that assumes several things and access the bank to do a validation like this:
public void validarContrato(int idContrato) {
Contrato contrato = contratoRepository.find(idContrato);
if (contrato.hasErrors()) {
throw new RuntimeException("Contrato inválido");
}
}
You could get an object Contrato
:
public void validarContrato(Contrato contrato) {
if (contrato.hasErrors()) {
throw new RuntimeException("Contrato inválido");
}
}
In this way, you can validate contracts before putting them into the database. This is very useful for doing unit tests and displaying errors for the user before registering something in the database.
Extension Points Using Interfaces
One of the ways I find it most productive to implement extension points in use cases is to create an interface that functions as a callback . In Java 8 you can use lambdas to do this, however I'll do it using the traditional way for illustration.
Imagine that you have a use case as follows:
public void criarContrato(Contrato c) {
recalcularContrato(c);
validarContrato(c);
//algo específico aqui
gravarContrato(c);
}
If you want to put some step there that varies according to the situation, you can add a parameter that works as an extension point.
For this, you will need an interface like the following:
public interface ExtensaoContrato {
executar(Contrato c);
}
Then you can change the method as follows:
public void criarContrato(Contrato c, ExtensaoContrato pontoExtensao) {
recalcularContrato(c);
validarContrato(c);
if (pontoExtensao != null) pontoExtensao.executar(c);
gravarContrato(c);
}
This works more or less like the Strategy Pattern . The idea is that you leave a step to be taken by the client that will call the method, and there may be as many implementations as necessary from the interface.
Then the method that calls the contract creation can do the following:
criarContrato(contrato, new ExtensaoContrato() {
public void executar(Contrato c) {
//faz algo depois da validação e antes de gravar
}
});
The above code runs the criarContrato
method passing the contract and also an anonymous class that implements ExtensaoContrato
. The executar
method, implemented in this anonymous class, will then be executed by the criarContrato
method.
So you can "inject" any code there, in situations where you have a General Use Case and need only do something specific in one or more points.
In the case of this question, one of the scenarios (depending on how the actual implementation is) is to have an implementation for each type of contract.
Simple delegation
Another option is to make delegation simple, as already described in your question. If there are not many variations and inheritances it is the right choice as it is simple and straightforward.
Final considerations
One of the most common mistakes, especially of those at the intermediate level in development, is to try to apply patterns and class hierarchies unnecessarily in code.
This is not all bad. It means the developer is maturing. However, the weakness of every standard and inheritance is to greatly increase the complexity of the code. It is difficult to measure, but it would be necessary to consider whether this additional complexity brings more benefits than harm to the class model.