5

I'm using Netty (4.0.4.Final) in a project, and I keep running into a circular dependency that I need to factor out. This question mainly involves the concept of factoring out the circular dependency, but I'll be using some Netty terminology throughout for those who are familiar. Since my problem isn't actually with Netty, though, I decided not to tag it.

Below, I've posted my code, omitting portions that I don't believe relevant.

The Situation

I have a MyServer class that adds a ChannelInboundHandlerAdapter to a Bootstrap:

public class MyServer extends AbstractMyServer {
    private Integer someInteger; //Using Integer just for example's sake.

    public MyServer(MyServerInitializer initializer) {
        //...
        bootstrap.handler(initializer);
        //...
    }

    public void updateInteger(Integer value) {
        someInteger = value;
        //Send an update packet to another server.
    }
}

MyServerInitializer needs to add a ChannelInboundHandlerAdapter to the ChannelPipeline:

public class MyServerInitializer extends ChannelInitializer<SocketChannel> {
    private ChannelInboundHandlerAdapter handler;

    public MyServerInitializer(ChannelInboundHandlerAdapter handler) {
        this.handler = handler;
    }

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ch.pipeline().addLast(
                new ObjectEncoder(),
                new ObjectDecoder(),
                handler);
    }
}

I also have a MyServerHandler which is the constructor argument of MyServerInitializer in the case I'm speaking about:

public class MyServerHandler extends ChannelInboundHandlerAdapter {
    private MyServer server;

    public MyServerHandler(MyServer server) {
        this.server = server;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Integer obj = (Integer) msg; //Remember just using Integer for example. Think of it as an Object rather than an Integer.
        server.updateInteger(obj);
    }
}

So, the circular dependency becomes apparent during initialization:

public static void main(String[] args) {
    //I can't set a reference to MyServer instance here because it hasn't been created yet. I want to avoid the circular dependency here.
    MyServerHandler handler = new MyServerHandler(...);
    MyServerInitializer initializer = new MyServerInitializer(handler);
    MyServer server = new MyServer(initializer);
}

Possible solutions

Refactor into main()

I could pull the creation of Integer someInteger out of MyServer, create it at the scope of the main() function, then inject it's reference into MyServerHandler and MyServer. This would of course give MyServerHandler the ability to modify it directly, instead of having to go through MyServer. The draw back is that it's been declared at the scope of main() now. I don't want to have to do this for every class member that might essentially need to be modified by a Handler class.

Create a MyServerFactory

One of the concepts I read about was to separate construction from use. This makes a lot of sense, so I gave it a shot with the below Factory pattern implementation.

public class MyServerFactory implements AbstractFactory<MyServer> {
    public MyServer create() {
        Integer someInteger = createInteger();
        MyServerHandler handler = createHandler(someInteger);
        MyServerInitializer initializer = createInitializer(handler);
        return new MyServer(initializer);
    }

    /* creator methods for the different components above. */
}

However, this seems like I simply moved the code from main() to this Factory class.

Questions

  1. What happens if I want to inject a different Handler into MyServerInitializer - perhaps this new Handler doesn't accept an Integer as an argument. Would I have to create a new Factory just for this case?
  2. Does it make sense to have a Factory that would probably only ever create a single instance of MyServer?
  3. Is there another option available to factor out this circular reference?

The question in bold is my main focus for asking this on StackOverflow. I feel like I must be overlooking something simpler, or more elegant here. I'm hoping that some of you more experienced users can lend some insight. Please, let me know if more information is needed.

Referenced Materials

Community
  • 1
  • 1
crush
  • 16,713
  • 9
  • 59
  • 100
  • I'd think there should almost certainly be a cleaner way. It's hard to tell exactly where the break should happen given my unfamiliarity with the framework and the less than helpful class names, but presently all of the classes are too aware of the other classes. Somewhere there should be the application itself, and the pieces should register themselves with the application (therefore allowing additional pieces to also register without requiring modification to the existing classes)... – Matt Whipple Jul 30 '13 at 13:46
  • Either MyServer or MyServerInitializer should have a public method that points to the appropriate `handler` and then the class that is the registrant should call that method when it is instantiated. Since `MyServerHandler` is aware of its container, I'd suggest trying to have it register itself with that container rather than having the associated knowledge split in to two locations. – Matt Whipple Jul 30 '13 at 13:49
  • As a quick follow up...you already have an initializer whose purpose is construction/wiring so I'd certainly question the need for adding a factory to glue the same pieces together. – Matt Whipple Jul 30 '13 at 18:38
  • I started looking at the Mediator pattern as a potential means of resolving my issue. My plan would be to place the `MyServer` on a `MyServerDirector` which would also house the `Integer` and an `IntegerUpdater` instance which would handle the updating of the `Integer`. Remember the `Integer` is just a placeholder for a more complex object in this example. It seems that the `Integer` shouldn't really even be on the `MyServer`. I believe in being on `MyServer` is is violating SRP. The SRP of `MyServer` is to act as a link between two points-not maintain updating of an object. What do you think? – crush Jul 30 '13 at 18:54
  • I would certainly agree that MyServer should not be doing anything more than acting as some form of dispatcher/Reactor and you'd be better off having some form of service or something similar for maintaing objects, but it's very hard to say more without spending time dealing with the whole picture. I'm also reminded now that you also seem to have a `Bootstrap` object which would be a second existing class that is dealing with construction/wiring. I'd advise looking more closely at the relationship between the pieces that already exist before possibly over-architecting or catching patternitis – Matt Whipple Jul 30 '13 at 19:55

1 Answers1

0

Disclaimer: I don't know much about Netty, these are just some thoughts from reading your code:

I don't see any problem with MyServerInitializer. MyServerInitializer does not have any dependenciesy on MyServerHandler or MyServer. That's fine. It would be worse if the constructor of MyServerInitializer would require MyServerHandler instead of ChannelInboundHandlerAdapter.

If possible you should change the constructor parameter of MyServer from MyServerInitializer to ChannelInitializer<SocketChannel>.

The problem is that MyServerHandler depends on MyServer while MyServer has a indirect runtime dependency on MyServerHandler. I would try to get rid of the MyServer dependency in MyServerHandler. To do this you can:

  • Move the updateInteger() method from MyServer into another class, let's call it IntegerUpdater. MyServerHandler should use IntegerUpdater instead of MyServer. IntegerUpdater should have no dependency on MyServer. Using this way you would not have any circular depedency.

  • Add an abstraction between MyServerHandler and MyServer. For example:

    public interface IntegerMessageReceiver {
      void handleMessage(Integer i);
    }
    

-

    public class MyServerHandler extends ChannelInboundHandlerAdapter {
      private List<IntegerMessageReceiver> integerMessageReceivers;

      public void addIntegerMessageReceiver(IntegerMessageReceiver imr) {
        integerMessageReceivers.add(imr);
      }

      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Integer obj = (Integer) msg; 
        for (IntegerMessageReceiver imr : integerMessageReceivers) { 
          imr.handleMessage(obj);
        }
      }
    }

-

    public class MyServer extends AbstractMyServer implements IntegerMessageReceiver {
      public void handleMessage(Integer i) {
        ...
      }
      ...
    }

Initilization:

MyServerHandler handler = new MyServerHandler();
MyServerInitializer initializer = new MyServerInitializer(handler);
MyServer server = new MyServer(initializer);
handler.addIntegerMessageReceiver(server);

Using this approach you would still have circular runtime dependencies but at least you got rid of the direct compile time dependency of MyServer in MyServerHandler.

micha
  • 47,774
  • 16
  • 73
  • 80