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
- What happens if I want to inject a different
Handler
intoMyServerInitializer
- perhaps this newHandler
doesn't accept anInteger
as an argument. Would I have to create a newFactory
just for this case? - Does it make sense to have a
Factory
that would probably only ever create a single instance ofMyServer
? - 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
- Resolving Circular Dependencies with Dependency Injection
- Circular Dependency in Constructors and Dependency Injection by Misko Hevery
- Clean Code - A Handbook of Agile Software Craftsmanship by Robert C. Martin
- Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma et. al