Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to work with many interfaces?

I have a situation where I have have a lot of model classes (~1000) which implement any number of 5 interfaces. So I have classes which implement one and others which implement four or five.

This means I can have any permutation of those five interfaces. In the classical model, I would have to implement 32-5 = 27 "meta interfaces" which "join" the interfaces in a bundle. Often, this is not a problem because IB usually extends IA, etc. but in my case, the five interfaces are orthogonal/independent.

In my framework code, I have methods which need instances that have any number of these interfaces implemented. So lets assume that we have the class X and the interfaces IA, IB, IC, ID and IE. X implements IA, ID and IE.

The situation gets worse because some of these interfaces have formal type parameters.

I now have two options:

  1. I could define an interface IADE (or rather IPersistable_MasterSlaveCapable_XmlIdentifierProvider; underscores just for your reading pleasure)

  2. I could define a generic type as <T extends IPersistable & IMasterSlaveCapable & IXmlIdentifierProvider> which would give me a handy way to mix & match interfaces as I need them.

  3. I could use code like this: IA a = ...; ID d = (ID)a; IE e = (IE)e and then use the local variable with the correct type to call methods even though all three work on the same instance. Or use a cast in every second method call.

The first solution means that I get a lot of empty interfaces with very unreadable names.

The second uses a kind of "ad-hoc" typing. And Oracle's javac sometimes stumbles over them while Eclipse gets it right.

The last solution uses casts. Nuff said.

Questions:

  1. Is there a better solution for mixing any number of interfaces?

  2. Are there any reasons to avoid the temporary types which solution #2 offers me (except for shortcomings in Oracle's javac)?

Note: I'm aware that writing code which doesn't compile with Oracle's javac is a risk. We know that we can handle this risk.

[Edit] There seems to be some confusion what I try to attempt here. My model instances can have one of these traits:

  • They can be "master slave capable" (think cloning)
  • They can have an XML identifier
  • They might support tree operations (parent/child)
  • They might support revisions
  • etc. (yes, the model is even more complex than that)

Now I have support code which operates on trees. An extensions of trees are trees with revisions. But I also have revisions without trees.

When I'm in the code to add a child in the revision tree manager, I know that each instance must implement ITtree and IRevisionable but there is no common interface for both because these are completely independent concerns.

But in the implementation, I need to call methods on the nodes of the tree:

public void addChild( T parent, T child ) {
    T newRev = parent.createNewRevision();
    newRev.addChild( foo );
    ... possibly more method calls to other interfaces ...
}

If createNewRevision is in the interface IRevisionable and addChild is in the interface ITree, what are my options to define T?

Note: Assume that I have several other interfaces which work in a similar way: There are many places where they are independent but some code needs to see a mix of them. IRevisionableTree is not a solution but another problem.

I could cast the type for each call but that seems clumsy. Creating all permutations of interfaces would be boring and there seems no reasonable pattern to compress the huge interface names. Generics offer a nice way out:

public
<T extends IRevisionable & ITree>
void addChild( T parent, T child ) { ... }

This doesn't always work with Oracle's javac but it seems compact and useful. Any other options/comments?

like image 575
Aaron Digulla Avatar asked Feb 20 '12 13:02

Aaron Digulla


People also ask

What is the use of multiple network interfaces?

Network and security function: Multiple network interfaces enable virtualized network appliance functions such as load balancers, network address translation (NAT) servers, and proxy servers that are configured with multiple network interfaces.

Can a computer have multiple NICs?

Each network interface controller (NIC) in a computer has a unique medium access control (MAC) address. If multiple NICs are installed in one computer, they each have their own MAC addresses.

Can you use two interfaces at once in Java?

Java does not support "multiple inheritance" (a class can only inherit from one superclass). However, it can be achieved with interfaces, because the class can implement multiple interfaces. Note: To implement multiple interfaces, separate them with a comma (see example below).

Why are interfaces important?

These interactions between your system and others are interfaces. Identifying interfaces helps you to define your system's boundaries. Identifying interfaces also helps you understand the dependencies your system has with other systems and dependencies other systems have with your system.


3 Answers

Loosely coupled capabilities might be interesting. An example here. It is an entirely different approach; decoupling things instead of typing. Basically interfaces are hidden, implemented as delegating field.

IA ia = x.lookupCapability(IA.class);
if (ia != null) {
    ia.a();
}

It fits here, as with many interfaces the wish to decouple rises, and you can more easily combine cases of interdepending interfaces (if (ia != null && ib != null) ...).

like image 51
Joop Eggen Avatar answered Sep 30 '22 11:09

Joop Eggen


I would avoid all "artificial" interfaces/types that attempt to represent combinations. It's just bad design... what happens if you add 5 more interfaces? The number of combinations explodes.

It seems you want to know if some instance implements some interface(s). Reasonable options are:

  • use instanceof - there is no shame
  • use reflection to discover the interfaces via object.getClass().getInterfaces() - you may be able to write some general code to process stuff
  • use reflection to discover the methods via object.getClass().getMethods() and just invoke those that match a known list of methods of your interfaces (this approach means you don't have to care what it implements - sounds simple and therefore sounds like a good idea)

You've given us no context as to exactly why you want to know, so it's hard to say what the "best" approach is.

Edited

OK. Since your extra info was added it's starting to make sense. The best approach here is to use the a callback: Instead of passing in a parent object, pass in an interface that accepts a "child".

It's a simplistic version of the visitor pattern. Your calling code knows what it is calling with and how it can handle a child, but the code that navigates around and/or decides to add a child doesn't have context of the caller.

Your code would look something like this (caveat: May not compile; I just typed it in):

public interface Parent<T> {
    void accept(T child);
}

// Central code - I assume the parent is passed in somewhere earlier
public void process(Parent<T> parent) {
    // some logic that decides to add a child
    addChild(parent, child);
}

public void addChild(Parent<T> parent, T child ) {
    parent.accept(child);
}

// Calling code
final IRevisionable revisionable = ...;
someServer.process(new Parent<T> {
    void accept(T child) {
        T newRev = revisionable.createNewRevision();
        newRev.addChild(child);
    }
}

You may have to juggle things around, but I hope you understand what I'm trying to say.

like image 41
Bohemian Avatar answered Sep 30 '22 12:09

Bohemian


If you have a method (semicode)

void doSomething(IA & ID & IE thing);

then my main concern is: Couldn't doSomething be better tailored? Might it be better to split up the functionality? Or are the interfaces itself badly tailored?

I have stumbled over similar things several times and each time it proved to be better to take big step backward and rethink the complete partitioning of the logic - not only due to the stuff you mentioned but also due to other concerns.

Since you formulated your question very abstractly (i.e. without a sensible example) I cannot tell you if that's advisable in your case also.

like image 25
A.H. Avatar answered Sep 30 '22 11:09

A.H.