5

プロジェクトで Netty (4.0.4.Final) を使用していますが、除外する必要がある循環依存関係が発生し続けています。この質問には主に、循環依存関係を除外するという概念が含まれますが、よく知っている人のために、Netty の用語を使用します。しかし、私の問題は実際には Netty にはないので、タグ付けしないことにしました。

以下に、関連性がないと思われる部分を省略して、コードを掲載しました。

状況

aに aMyServerを追加するクラスがあります:ChannelInboundHandlerAdapterBootstrap

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.
    }
}

MyServerInitializerChannelInboundHandlerAdapterに aを追加する必要があります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);
    }
}

私が話している場合MyServerHandlerのコンストラクター引数であるa もあります。MyServerInitializer

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);
    }
}

したがって、初期化中に循環依存関係が明らかになります。

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);
}

可能な解決策

リファクタリングmain()

の作成をInteger someIntegerから取り出しMyServer、関数のスコープで作成してから、その参照をandmain()に挿入できます。もちろん、これにより、を経由する代わりに、直接変更することができます。欠点は、現在の範囲で宣言されていることです。基本的にクラスによって変更される必要があるすべてのクラス メンバーに対してこれを行う必要はありません。MyServerHandlerMyServerMyServerHandlerMyServermain()Handler

作成するMyServerFactory

私が読んだ概念の 1 つは、建設と使用を分離することでした。これは非常に理にかなっているので、以下の Factory パターンの実装を試してみました。

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. */
}

main()ただし、これはコードをこのFactoryクラスに移動しただけのようです。

質問

  1. Handler別の a を挿入したい場合はどうなりますかMyServerInitializer- おそらく、この newは引数として anHandlerを受け入れません。この場合のためだけにInteger新しいものを作成する必要がありますか?Factory
  2. Factoryおそらく のインスタンスを 1 つしか作成しない を使用することは理にかなっていますMyServerか?
  3. この循環参照を除外するために利用できる別のオプションはありますか?

太字の質問は、StackOverflow でこれを尋ねる際の主な焦点です。ここでは、もっとシンプルな、またはもっとエレガントなものを見落としているに違いないと感じています。経験豊富なユーザーの何人かが洞察を提供できることを願っています。さらに情報が必要な場合はお知らせください。

参考資料

4

1 に答える 1

0

免責事項:私はNettyについてあまり知りません。これらはあなたのコードを読んで考えたことです:

に問題はありませんMyServerInitializer。またはMyServerInitializerへの依存関係はありません。それはいいです。のコンストラクターが の代わりにrequire する場合はさらに悪いでしょう。MyServerHandlerMyServerMyServerInitializerMyServerHandlerChannelInboundHandlerAdapter

可能であれば、のコンストラクタ パラメータをMyServerからMyServerInitializerに変更する必要がありますChannelInitializer<SocketChannel>

問題は、MyServerHandler依存しMyServerMyServerいるのに、実行時に間接的に依存していることMyServerHandlerです。MyServerの依存関係を取り除こうとしますMyServerHandler。これを行うには、次のことができます。

  • updateInteger()メソッドを から別のクラスに移動し、MyServerと呼びましょうIntegerUpdater。の代わりにMyServerHandler使用する必要があります。に依存してはいけません。この方法を使用すると、循環依存関係がなくなります。IntegerUpdaterMyServerIntegerUpdaterMyServer

  • MyServerHandlerと の間に抽象化を追加しMyServerます。例えば:

    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) {
        ...
      }
      ...
    }

初期化:

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

このアプローチを使用すると、循環的な実行時依存関係が残りますが、少なくともMyServerinのコンパイル時の直接的な依存関係を取り除くことができますMyServerHandler

于 2013-08-03T22:59:52.157 に答える