What you are saying is that sometimes you want this:
public interface IController<E> {
void adicionar(E Element);
...
}
And pray this:
public interface IController<E> {
E adicionar(E Element);
...
}
You can not vary the return type in this way.
It's a common temptation to want to be very granular when defining an API. You would like each detail to accurately reflect the behavior of each object.
But the more variation, more complexity, and in the vast majority of cases it's worth sacrificing the economy of one or two lines of code for consistency.
Maintain consistency
The common solution to this specific case is to always return the included object. It is a good practice because the object, after having the data included in the database, usually gains a generated ID, timestamp, or some other attribute generated at the time of insertion that may be required in the system.
Even though you only see the need for a case today, it tends to grow as the system evolves.
In cases where there is nothing to do, you can simply return the same object you received in the parameter:
@Override
public Entidade adicionar(Entidade entidade) {
//insere no banco
return entidade;
}
Advantages:
- It costs nothing
- Consistent API (exceptions and behavior variations increase system complexity)
- Facilitates system maintenance
Disadvantages:
- One more line of code?
- I could not think of anything concrete ...
Granular API
If, even with what I wrote above you still want to be granular, the solution is to define interfaces more granularly.
Example:
interface IGeneralController<E> {
//...
}
interface IReturningAppenderController<E> {
E adicionar(E Element);
}
interface IAppenderController<E> {
void adicionar(E Element);
}
public class Controller implements IGeneralController<Entidade>, IReturningAppenderController<Entidade> {
@Override
public Entidade adicionar(Entidade entidade) {
//insere no banco
return entidade;
}
}
public class OtherController implements IGeneralController<Entidade>, IAppenderController<Entidade> {
@Override
public void adicionar(Entidade entidade) {
//insere no banco
}
}
It looks cool, but now you have more verbose code and a lot of conditions and casts you'll have to do at runtime.