2012-03-08 12 views
5

Mam aplikację, która składa się z dwóch procesów, jednego procesu klienta z GUI (opartym na SWT) i jednego procesu serwera. Proces klienta jest bardzo lekki, co oznacza, że ​​wiele operacji GUI będzie musiało wysłać zapytanie do procesu serwera lub poprosić o coś, na przykład w odpowiedzi na kliknięcie przycisku przez użytkownika lub wybranie elementu menu. Oznacza to, że nie będzie dużo obsługi zdarzeń, który wygląda tak:Wzorzec dla przepływu żądanie-odpowiedź z klasami wewnętrznymi

// Method invoked e.g. in response to the user choosing a menu item 
void execute(Event event) { 
    // This code is executed on the client, and now we need some info off the server: 
    server.execute(new RemoteRequest() { 
     public void run() { 
      // This code is executed on the server, and we need to update the client 
      // GUI with current progress 
      final Result result = doSomeProcessing(); 
      client.execute(new RemoteRequest() { 
       public void run() { 
        // This code is again executed on the client 
        updateUi(result); 
       } 
      } 
     } 
    }); 
} 

Jednakże, ponieważ server.execute implikuje serializacji (jest on wykonywany na komputerze zdalnym), wzór ten nie jest możliwe bez dokonywania całość klasa serializowalna (ponieważ klasy wewnętrzne RemoteRequest nie są statyczne (aby było jasne: nie jest wymagane, aby implementacja Request mogła uzyskać dostęp do instancji nadrzędnej, ze względu na aplikację mogą być statyczne). jednym z rozwiązań jest utworzenie oddzielnych (prawdopodobnie statycznych wewnętrznych) klas dla Żądania i Odpowiedzi, ale to boli czytelności i utrudnia wymazać przepływ wykonania.

Próbowałem znaleźć jakiś standardowy wzorzec do rozwiązania tego problemu, ale nie znalazłem niczego, co by odpowiadało na moją obawę co do czytelności.

Aby było jasne, będzie wiele takich operacji, a operacje często są dość krótkie. Zauważ, że obiekty Future nie są tutaj w pełni użyteczne, ponieważ w wielu przypadkach jedno żądanie do serwera będzie wymagało wielu rzeczy na kliencie (często różniących się), a także nie zawsze jest zwracany wynik.

Idealnie chciałabym, aby móc napisać kod tak: (oczywiste pseudo-kod teraz należy lekceważyć oczywistych błędów w szczegóły)

String personName = nameField.getText(); 
async exec on server { 
    String personAddress = database.find(personName); 
    async exec on client { 
     addressField.setText(personAddress); 
    } 
    Order[] orders = database.searchOrderHistory(personName); 
    async exec on client { 
     orderListViewer.setInput(orders); 
    } 
} 

teraz chcę być jasne, że pod spodem architektura jest na miejscu i działa dobrze, powodem tego rozwiązania jest oczywiście nie powyższy przykład. Jedyne, czego szukam, to sposób napisania kodu jak wyżej, bez konieczności definiowania klas statycznych dla każdej zmiany procesu. Mam nadzieję, że nie tylko komplikowałem sprawę, podając ten przykład ...

+0

W jaki sposób odnosi się to do jms? Czy ten kod jest wykonywany na kliencie, serwerze lub innej maszynie? – Andrejs

+0

Tak, jest. Istnieją przypadki, w których kod jest wykonywany na kliencie i serwerze. Istnieją również przypadki, w których klient wykonuje coś na serwerze, który z kolei wykonuje coś na kliencie. – Krumelur

+0

Myślę, że potrzebujemy trochę więcej szczegółów. Czy prośba/odpowiedź są twoimi własnymi zajęciami, czy są częścią jakiegoś frameworka? Co dokładnie robi execute? – Andrejs

Odpowiedz

0

Tak naprawdę to rozwiązałem, tworząc klasę bazową z niestandardowym serializerem, który się tym zajmuje. Nadal mam nadzieję, że ostatecznie rozwiąże to język.

1

Preferuję używanie Command Pattern i ogólnych asynchronicznych wizytówek. Tego rodzaju podejście jest stosowane na przykład w GWT do komunikacji z serwerem. Komendy Serializowalne, AsyncCallback to interfejs.

Coś wzdłuż tych linii:

// from the client 
    server.execute(new GetResultCommand(args), new AsyncCallback<Result>() 
      { 
       public void onSuccess(Result result) { 
        updateUi(); // happens on the client 
       } 
      }); 

Następnie serwer musi otrzymać polecenie, przetwarza je i wydać odpowiednią reakcję z wynikiem.

+0

W takim przypadku "AsyncCallback" nie będzie niestatyczną klasą wewnętrzną? W jaki sposób jest serializowany do serwera bez konieczności serializacji klasy zewnętrznej? – Krumelur

+0

AsyncCallback nie musi być serializowany i wysyłany. Obiekt 'server' po prostu zachowuje odniesienie do niego, a następnie wywołuje go, gdy obiekt wynikowy przybywa z rzeczywistego serwera. Tylko Dowództwo i Wynik muszą być Serializowalne. – Andrejs

+0

Masz rację, chociaż moim problemem jest to, że chciałbym, aby 'GetResultCommand' był in-line jako anonimowa klasa, a' AsyncCallback' nie jest dla mnie tak ważny (chociaż byłoby miło). – Krumelur

0

Wczoraj spotkałem się z podobnym problemem. Istnieje rozwiązanie, które korzysta z anonimowych klas (a tym samym nie wymaga definiowania statycznych klas wewnętrznych), ale powoduje, że te anonimowe klasy są statyczne (a zatem nie odwołują się do zewnętrznego obiektu).

Wystarczy zdefiniować anonimowych klas w metodzie statycznej, jak to:

void execute(Event event) { 
    static_execute(server, client, event); 
} 

// An anonymous class is static if it is defined in a static method. Let's use that. 
static void static_execute(final TheServer server, final TheClient client, final Event event) { 
    server.execute(new RemoteRequest() { 
     public void run() { 
      final Result result = doSomeProcessing(); 
      // See note below! 
      client.execute(new RemoteRequest() { 
       public void run() { 
        updateUi(result); 
       } 
      }); 
     } 
    }); 
} 

Główną zaletą tego podejścia, w porównaniu do korzystania z nazwanych statycznych klas wewnętrznych, chyba że można uniknąć konieczności definiowania pól i konstruktorów dla tych klas.

- Pomyśl o tym, ta sama sztuczka prawdopodobnie musi być zastosowana jeszcze raz, dla kierunku serwera-> klienta. Zostawię to jako ćwiczenie dla czytelnika :-)

+0

Dzięki za wzmiankę o tym. Rozważyłem takie rozwiązanie, ale okazało się, że kod ten staje się jeszcze bardziej skomplikowany w moim przypadku, napisany w ten sposób. – Krumelur

0

PIERWSZA: Bez wzoru, jeśli bym zasugerował, możesz zrobić oddzielną klasę dla obsługi wszystkich Wzorów. Wystarczy przekazać instancję każdego zdarzenia wygenerowanego obiektu do klasy i przekazać żądanie zdarzenia do innych klas. Delegacja doprowadzi do bardzo klarownego podejścia, wystarczy, że skorzystamy z instanceof, a następnie delegujemy dalej. Każde wydarzenie może być zwięzłe w oddzielnym miejscu. Wraz z powyższym podejściem, tak, POLECENIE WZORZECOWE jest zdecydowanie dobrą opcją rejestrowania żądań, ale uzyskujesz stan WYDARZENIA dla każdego podniesionego żądania, więc możesz wypróbować WZORZEC STANOWY, ponieważ pozwala obiektowi zmienić jego zachowanie przy zmianie stanu.

Powiązane problemy