Applying Interface in Controllers

Asked

Viewed 159 times

1

I have some controllers who call upon their respective models and would like to apply interface on them. I have created an interface with some methods that it would be important for everyone to implement, as follows:

public interface IController<E> {

    public void adicionar(E Element);

    public void editar(E element);

    public List<E> listar();

    public void excluir(E element);
}

E is the type of object controller treats. (Ex. UsuarioController will implement IController<Usuario>).

Currently in the system, all controllers already have these interface methods. The problem is that in one of controllers, the method adicionar(E element) need to return the same object that was added, and not void, because this object returns with the id registered in the database, then I display the data of this object on screen.

Given the above, how can I apply an interface to classes with common behaviors (such as controllers) and one of them has one of the different "contract" methods?

3 answers

2

If I understood correctly it would be this:

public E adicionar(E Element);

Otherwise, you cannot use the same interface. What you could inherit from this existing one:

public interface IControllerEspecial<E> extends IController<E> {
    public E adicionar(E Element);
}

I put in the Github for future reference.

This does not completely solve the problem. The ideal would be a larger gambiarra (which is sometimes necessary). You would have to create an interface without this method adicionar, then create that IController extending with this method with signature returning void. Already the IControllerXXX would have the same method with signature returning E. The way I showed it would have two methods adicionar with different signatures.

Another way is to have completely separate interfaces, but then you start to throw away the mechanism, because you start to remember that you have to use the auxiliary interface. What happens if you use the IController and forget to use the help only with the method that causes problems? You realize that this solution is to spoil the design correct to solve a specific problem? Suddenly you have a CRUD interface without the C. It seems to me that conceptually this is wrong, the contract gets lame.

Particularly I would try to change something to normalize operations, make return always the same thing is one of them, I do not know if it is ideal for your case.

  • I thought about it, but then all controllers will have to return an object, because of a method in only one of them..

  • But this is the idea of Interface no? Create a template for something that will have similar behavior/structure.

  • Equal, not similar, when it is only similar, needs another interface.

  • This Icontrollerespecial seems to be close to what I need, I’m going to test it here. (off-topic: it’s strange to see an interface being extended, even if both interfaces are hehe)

  • Bigown, the problem is I only have 2 choices: either I force all controllers to keep returning NULL just so one of them returns something (which I wanted to avoid), or I adapt/isolate).

  • It didn’t work, the IDE complains that the 2 add methods of the interfaces are in conflict. It seems that my way of thinking is not right for the problem, I’ll see what I can change.

  • Read the answer, I edited it. But there’s no good solution to this but to change what you’re doing.

  • I don’t have the slightest clue how to handle it any other way. I cannot void this method because I need to display the data with the id that the bank saved it. You think it would be "Dirty Code" for me to make everyone return null just to fit in? There is no other way to return the id of the database, of the other fields of the returning object in the table, only the id is indexable and unique.

  • 2

    I think. But I can’t talk without knowing the whole problem. Of course there are situations where the ideal is not possible. I think the whole concept of the application can be wrong :) When you start making the house pie, it doesn’t straighten anymore.

  • 2

    I agree with @bigown! You’re looking to assign two behaviors to the same interface. This makes the contract concept lose its meaning. What you can do is have 3 interfaces. One common to all, and two with the specific add() . So you would implement two of them. You can also segment interfaces by operation, edit, list, add. Still, as @bigown said, solving the syntax issue won’t free you from future problems if architecture brings you some problems.

Show 5 more comments

2


What you’re saying is, pray you want it:

public interface IController<E> {
   void adicionar(E Element);
    ...
}

And now this:

public interface IController<E> {
   E adicionar(E Element);
    ...
}

It is not possible to vary the type of return in this way.

It is a common temptation to want to be too granular when defining an API. You would like each detail to accurately reflect the behavior of each object.

However, the more variation, the more complexity and in the vast majority of cases it is worth sacrificing the savings of one or two lines of code to achieve consistency.

Maintain consistency

The common solution for this specific case is to always return the object included. It is 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 and which may be needed in the system.

Even though today you only see the need for a case, 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;
}

Perks:

  • Costs nothing
  • Consistent API (exceptions and behavior variations increase system complexity)
  • Facilitates system maintenance

Disadvantages:

  • One more line of code?
  • I couldn’t think of anything concrete...

API granular

If, even with what I wrote above you still want to be granular, the solution is to define interfaces more granular.

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 seems kind of cool, but now you have a more verbose code and a lot of conditions and Casts which you will have to do at runtime.

  • So, returning the same item, even though for now I still have no use for it in the system, it would not be "Dirty code"?

  • 1

    @Diegof If you look at the definition of "Dirty code" in Wikipedia and on other sites will see that it is just the opposite. Lack of a standard, consistency and maintenance difficulty are synonymous with "Dirty code". Unless you see some reason why a Return will let you cause problems with your code even if it isn’t always used, there’s no reason to think so. You may find it unnecessary and opt for another alternative smoothly, but if you think about system maintenance issue, using a different interface is much more likely to change

0

Depending on your implementation you can use a different approach. Have one method with a void and another with the object’s Return. Could have on your interface:

public interface IController<E> {

 public void adicionar(E Element);

 public void editar(E element);

 public List<E> listar();

 public void excluir(E element);

 public Element retornaElement();
//Se você controla os id's pode utiliza-lo como parâmetro. Caso não, caso sejam incrementais irá sempre buscar o último registro inserido.
}

You need to be aware of the competition, depending on your type of application for this operation. Another point of attention is to ensure that the commit of the insert command in the database actually existed so that you can actually retrieve the last inserted record.

In this way, its implementation could be:

public Element implementacaoController(){
  // código 
  adicionar(element);
  // garantia de commit e conclusão da transação
  return retornaElement(); //aqui dependendo da sua implementação poderia passar o id do element, ou seja, element.getID(), ou simplesmente recuperar o último registro inserido.
}

If you have difficulties with the behaviour of the method in inserting and recovering during the same execution. Try working with isolated methods being displayed through a third method.

Depending on your context, perhaps these approaches meet your need. Hug!

  • Then all classes will have to implement this method, generating unnecessary code, which I want to avoid. I chose to adapt all add() methods with the same feedback, as reported by colleague Luis, the most I miss is functionality that, for now, I will not use.

  • 1

    I get it. @Diegof, does an interface and a generic implementation of your code come with that chunk of code? Similar to the ones used for DAO’s. In it you could leave implemented this behavior and when you had to implement in a new controller would not have the need of redundancy of the code snippet. When implementing would already have the insertion and the return of the element in a new call of a method already treated for this behavior. But of course, it might not be interesting to have something generic just for that need. Here’s a suggestion if it’s really necessary.

Browser other questions tagged

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