2012-05-20 1 views
4

첫째,이를 데리고 도서관에 대한 간략한 설명 :자바 디자인 : 많은 클래스에서 사용하는 데이터 객체를 처리

내가 바이트의 블록 읽기, 제공된 직렬 포트에 연속적으로 수신 도서관이 그들을 의미있는 방식으로 처리하도록 함께 전달합니다 (세부 사항은 중요하지 않습니다). 라이브러리를 조금 더 재사용 할 수 있도록하기 위해 이러한 바이트 처리가 인터페이스 (FrameProcessor)로 추상화되었습니다. 라이브러리를 사용하는 응용 프로그램과 관계없이 항상 발생하게 될 처리를 처리하기 위해 라이브러리에 몇 가지 기본 구현이 존재합니다. 그러나 응용 프로그램이 특별히 신경 쓰는 작업을 수행하는 사용자 지정 프로세서를 추가 할 수 있습니다.

이 프로세서로 전달되는 바이트 이외에도 대부분의 프로세서가 재미있을 수 있다고 보장하는 정보가 들어있는 데이터 개체 (ReceiverData)가 있습니다. 라이브러리 자체에 의해 전적으로 관리됩니다 (즉, ReceiverData의 인스턴스를 설정/유지하는 것은 응용 프로그램의 책임이 아니며 데이터를 사용할 수있는 방법에주의를 기울여서는 안됩니다). 반드시 상관하지 않을 수도 뭔가에 전달 될 데이터를 필요로하는 것 나는 정말이 방법을 좋아하지 않는다, 그러나

public interface FrameProcessor { 

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

:

는 지금, ReceiverData은 각 프로세서에 매개 변수로 전달되는 그것에 대해. 또한 ReceiverData를 신경 쓰는 프로세서의 경우, 다른 메서드 호출이있을 때 개체 참조를 전달해야합니다 (해당 메서드 호출이 해당 데이터에 액세스해야하는 경우).

FrameProcessor를 추상 클래스로 변경 한 다음 보호 된 ReceiverData 멤버에 대한 setter를 정의하는 것을 고려했습니다. 그러나 그것은 모든 종류의 FrameProcessor의 목록을 반복하고 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); 
    } 
} 

그런 식으로, 라이브러리에있는 각 스레드가 시작했을 때, 그것은 단지에 다음 필요에 따라 사용할 수 있습니다 것이다, ThreadedContext에 그 ReceiverData에 대한 참조를 추가 할 수 있습니다 프로세서를 전달할 필요가 없습니다.

나는 이미 잘 작동하는 해결책을 얻었 기 때문에 확실히 중요한 질문입니다. 그것은 단지 나를 괴롭혔다. 생각? 더 나은 방법?

+1

나는 'ThreadedContext' 접근법을 가장 좋아합니다. 참고로,'java.lang.ThreadLocal '이 있습니다. 스레드 ID를 키로 사용하는'HashMap' 대신에 사용할 수 있습니다. – Torious

+0

ReceiverData가 실제로 수행하고 포함하는 것에 대해 자세히 설명해 주시겠습니까? 그것은 나에게 약간의 FrameProcessor 서브 클래스의 멤버가되어야한다고 들리지만, 더 자세한 내용없이 말하기는 어렵다. – toto2

답변

3

현재 접근 방식이 가장 좋습니다. 그것은 본질적으로 thread-safe (무국적이기 때문에)입니다. 동일한 프로세서를 여러 스레드에서 사용할 수 있습니다. 이해하고 사용하기 쉽습니다. 예를 들어 서블릿이 작동하는 방식과 매우 비슷합니다. 요청 및 응답 객체는 서블릿에 관심이 없더라도 서블릿에 전달됩니다. 프로세서를 테스트 할 수 있도록 스레드 로컬 컨텍스트를 설정할 필요가 없으므로 단위 테스트도 매우 쉽습니다. ReceiverData (실제 또는 가짜) 만 전달하면됩니다.

바이트 배열과 ReceiverData를 전달하는 대신 단일 인수에서 둘을 혼합 할 수 있습니다.

+1

요청/응답 객체는 실제로 서블릿 설계에서 잘못된 것으로 간주되는 일부 객체입니다 ([here] (http://misko.hevery.com/2009/04/08/how-to-do-everything-wrong- with-servlets /), 예를 들면); 부엌 싱크대 물체, 서비스 탐지기 방지 패턴 ... – Torious

+1

글쎄, 그들은이 블로거가 잘못 생각한 것 같습니다. 그리고 ReceiverData는 단순한 데이터 객체이므로 Kitchen sink와 Service Locator 문제는 여기에 적용되지 않습니다. 나는 서블릿 테스트가 어렵다는 것에 동의한다. 그러나 스레드 로컬에 상태를 두는 것은 테스트 가능성과 관련하여 메서드 인수에 상태를 주입하는 것보다 훨씬 나쁩니다. –

+0

나는 다양한 프로세서에 ReceiverData 객체를 전달하는 것으로 끝을 맺었다. 저는 이해의 용이성에 대한 귀하의 요점을 고려하지 않았지만 그것에 대해 생각해보십시오. 이것은 많은 응용 프로그램에서 사용되기를 기대하는 라이브러리이므로 어떻게 작동하는지 이해하는 것이 매우 중요합니다. 테스트의 용이함은 좋은 특권이다.모든 응답을 주셔서 감사합니다. – JDS

1

byte[]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을 사용하고 읽기 전용 액세스를 제공하려는 경우 일 수 있습니다. 라이브러리에 따라 다르고 사용 방법은 다르지만 간단한 바이트 배열보다 더 멋진 API 인 Frame을 제공 할 수 있습니다.

+0

이것은 문제를 푸는 가장 자연스러운 방법처럼 보입니다. 바이트와 ​​ReceiverData는 항상 함께 사용되므로 함께 배치하십시오. 'frame'보다는 ('frame.getRawBytes()') 데이터를 얻는 것이 약간 힘들지만 실제로 그렇게하지는 않습니다. –

0

OP 상태로 process(byte[] frame, ReceiverData data)의 문제점은 ReceiverData이 구현에 사용되거나 사용되지 않을 수 있다는 것입니다. 따라서 process()ReceiverData에 종속 된 것은 "잘못"입니다. 대신, FrameProcessor 구현은 요청시 현재 프레임에 대해 ReceiverData의 인스턴스를 제공 할 수있는 Provider을 사용해야합니다.

아래 예는 이것을 설명합니다. 명확성을 위해 의존성 삽입을 사용했지만 생성자에서 이러한 객체를 전달할 수도 있습니다. FrameContext은 OP에서 제안 된 것과 마찬가지로 ThreadLocal<T>을 사용합니다. 구현 힌트는 this link을 참조하십시오. DIY Provider<T> 구현은 FrameContext에 직접 의존합니다.

이 경로를 가고 싶다면 Google Guice 또는 CDI과 같은 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>; 
    ... 
} 

는,이 방법은 또한 "하위를 추가 할 위치를 미래의 상황을 방지 할 수 있습니다 : 미래는 개체 컨텍스트/범위 객체에 추가 할 수 있으며, 해당 업체가 프로세서에 주입 필요 -objects "를 ReceiverData으로 변경하면 주방 싱크 개체 상황이 발생합니다 (예 : ReceiverData.metaData, ReceiverData.frameSource, ...).

참고 : 이상적으로는 수명이 단일 프레임과 동일한 개체를 처리해야합니다. 그런 다음 생성자에서 단일 프레임을 처리하기위한 종속성을 선언하고 삽입하여 각 프레임에 대해 새 프로세서를 만들 수 있습니다. 하지만 당신이 많은 프레임을 처리하고 있다고 가정하고 성능상의 이유로 현재의 접근법을 고수하고 싶습니다.