4

まず、これをもたらしたライブラリの簡単な説明:

提供されたシリアルポートで継続的にリッスンし、バイトのブロックを読み取り、それらを渡して意味のある方法で処理するライブラリがあります(詳細は質問にとって重要ではありません)。ライブラリをもう少し再利用可能にするために、これらのバイトの処理はインターフェイス (FrameProcessor) に抽象化されました。ライブラリ自体には、それを使用するアプリケーションに関係なく常に発生する処理を処理するためのデフォルトの実装がいくつか存在します。ただし、アプリケーションが特に気にすることを行うために、カスタム プロセッサを追加するためのサポートがあります。

これらのプロセッサに渡されるバイトとは別に、データ オブジェクト (ReceiverData) があります。これには、ほとんどの (すべてであるとは限りません) プロセッサが関心を持つ可能性のある情報が含まれています。ライブラリ自体によって完全に維持されます (つまり、ReceiverData のインスタンスをセットアップ/維持するのはアプリケーションの責任ではありません。データがどのように利用可能になるかを気にする必要はありません。データが利用可能であることだけです)。

現在、ReceiverData はパラメーターとして各プロセッサーに渡されています。

public interface FrameProcessor {

    public boolean process(byte[] frame, ReceiverData receiverData);
}

ただし、必ずしもデータを気にしない可能性のあるものにデータを渡す必要があるため、このアプローチは本当に好きではありません。また、ReceiverData を処理するプロセッサの場合、他のメソッド呼び出しでオブジェクト参照を渡す必要があります (これらのメソッド呼び出しがそのデータにアクセスする必要がある場合)。

FrameProcessor を抽象クラスに変更してから、保護された ReceiverData メンバーのセッターを定義することを検討しました。しかし、それはちょっとひどいようにも思えます - すべての FrameProcessors のリストを反復処理し、ReceiverData インスタンスを設定する必要があります。

また、ある種の静的なスレッド化されたコンテキスト オブジェクトについても考えました (ライブラリは一度に複数のポートでのリッスンをサポートしているため、必然的にスレッド化されます)。基本的に、次のようなものがあります。

public class ThreadedContext {

    private static Map<Long, ReceiverData> receiverData;

    static {
        receiverData = new HashMap<Long, ReceiverData>();
    }

    public static ReceiverData get() {
        return receiverData.get(Thread.currentThread().getId());
    }

    public static void put(ReceiverData data) {
        receiverData.put(Thread.currentThread().getId(), data);
    }
}

こうすることで、ライブラリ内の各スレッドが開始されたときに、その ReceiverData への参照を ThreadedContext に追加するだけで済み、必要に応じてプロセッサがそれを渡す必要なく利用できるようになります。

私はすでにうまく機能する解決策を持っているので、これは確かに衒学的な質問です。それはちょうど私を悩ませました。考え?より良いアプローチ?

4

3 に答える 3

3

私はあなたの現在のアプローチが一番好きです。本質的にスレッドセーフです (ステートレスのため)。これにより、同じプロセッサを複数のスレッドで使用できます。理解しやすく、使いやすいです。たとえば、サーブレットの動作方法と非常によく似ています。リクエスト オブジェクトとレスポンス オブジェクトは、それらが気にならない場合でも、サーブレットに渡されます。また、プロセッサをテストするためにスレッド ローカル コンテキストを設定する必要がないため、単体テストも非常に簡単です。ReceiverData (本物または偽物) を渡すだけで、それだけです。

バイト配列と ReceiverData を渡す代わりに、両方を 1 つの引数に混在させることができます。

于 2012-05-20T21:08:22.440 に答える
1

byte[]andを新しいクラスにカプセル化し、ReceiverDataそれをフレーム プロセッサに渡します。同じ単一のオブジェクトを独自のメソッドに渡すことができるだけでなく、必要に応じて将来の拡張が可能になります。

public class Frame {
    private byte[] rawBytes;
    private ReceiverData receiverData;

    public ReceiverData getReceiverData() { return receiverData; }
    public byte[] getRawBytes() { return frame; }
}

public interface FrameProcessor {
    public boolean process(Frame frame);
}

これはやり過ぎのように思えるかもしれませんし、プロセッサが不要なメソッド呼び出しを行う必要があるかもしれませんが、未加工のバイト配列へのアクセスを提供したくないことに気付くかもしれません。ByteChannelおそらく、代わりに a を使用して、読み取り専用アクセスを提供したいでしょう。Frameライブラリとその使用方法によって異なりますが、単純なバイト配列よりも優れたAPI を内部で提供できる場合があります。

于 2012-05-20T22:17:23.957 に答える
0

OPが述べているように、問題は実装で使用される場合と使用されない場合があることですprocess(byte[] frame, ReceiverData data)。したがって、依存関係があるのReceiverDataは「間違っています」。代わりに、実装では、現在のフレームのインスタンスをオンデマンドで提供できる を使用する必要があります。process()ReceiverDataFrameProcessorProviderReceiverData

以下の例は、これを示しています。わかりやすくするために依存性注入を使用しましたが、これらのオブジェクトをコンストラクターに渡すこともできます。OPで提案されているように、sをFrameContext使用します。実装のヒントについては、このリンクThreadLocal<T>を参照してください。DIY の実装は、直接依存する可能性があります。Provider<T>FrameContext

このルートに進みたい場合は、Google GuiceCDIなどの DI フレームワークの使用を検討してください。カスタム スコープを使用する場合は、おそらく Guice の方が簡単です。

public class MyProcessor implements FrameProcessor {

    @Inject
    private Provider<ReceiverData> dataProvider;

    public boolean process(byte[] frame) {
        ...
        ReceiverData data = dataProvider.get();
        ...
    }
}

public class Main {

    @Inject
    private FrameContext context;

    public void receiveFrame(byte[] frame, ... ) {

        context.begin();
        ...
        context.setReceiverData(...); // receiver data is thread-local
        ...

        for (FrameProcessor processor : processors)
            processor.process(frame);

        context.end();
    }
}

このアプローチは非常に拡張可能です。将来必要なオブジェクトをコンテキスト/スコープ オブジェクトに追加し、対応するプロバイダーをプロセッサに挿入できます。

public class MyProcessor ... {

    @Inject private Provider<FrameMetaData>;
    @Inject private Provider<FrameSource>;
    ...
}

この例からわかるように、このアプローチにより、「サブオブジェクト」を に追加する将来の状況を回避することもできReceiverDataます。ReceiverData.metaDataReceiverData.frameSource

注:理想的には、単一フレームに等しい有効期間を持つオブジェクトを処理する必要があります。次に、コンストラクターで単一フレームを処理するための依存関係を宣言 (および挿入) し、フレームごとに新しいプロセッサーを作成するだけです。ただし、多くのフレームを処理しているため、パフォーマンス上の理由から現在のアプローチに固執したいと考えています。

于 2012-05-20T22:47:16.780 に答える