Scott Tiger Tech Blog

Blog technologiczny firmy Scott Tiger S.A.

MVP4G – implementacja wzorca MVP dla GWT

Autor: Krzysztof Nielepkowicz o 2. lutego 2012

    Dzisiejszy wpis, będzie niejako przygotowaniem dla kolejnego wpisu. Zaprezentuję połączenie GWT oraz frameworku MVP4G co samo w sobie nie jest niczym niezwykłym, aczkolwiek jest to konfiguracja bardzo owocna.
Dlaczego warto łączyć GWT z innymi frameworkami? Otóż surowe GWT nie ułatwia szybkiego tworzenia większych aplikacji. Sami twórcy GWT polecają stosowanie wzorca MVP (Model View Presenter), jednak nie dostarczają implementacji, gdyż to co jest proponowane w oficjalnym tutorialu dla GWT woła o pomstę do Nieba! Wystarczy spojrzeć na hierarchię klas :
    Dla każdego zdarzenia konieczne jest stworzenie dwóch klas. Nie trudno się domyślić że pisanie aplikacji bardziej skomplikowanej witanie użytkownika po naciśnięciu przycisku spowoduje wykładniczy przyrost klas. Taki sposób pisania aplikacji nie przypadł mi do gustu, zapytałem jugowiczów jak ugryźć temat. Tak się dowiedziałem o MVP4G – implementacji owego wzorca. Nie będę tutaj się rozpisywał na temat zalet i wad – po prostu pokażę jak stworzyć projekt.
Potrzebne biblioteki/narzedzia :
Na początek tworzymy nowy projekt w Eclipse – aplikacja GWT wraz z sample code :
  W następnym kroku odznaczamy „Use Google App Engine” bo nie będzie nam potrzebny ale za to zaznaczamy opcję „Generate Project Sample Code”. Eclipse wygeneruje nam całą infrastrukturę projektu, która się przyda – przynajmniej w większości. Ja swój projekt nazwałem tutek_MVP4G. Zaczynamy od usunięcia niepotrzebnych plików i odwołań do nich w kodzie :
  • FieldVerifier
  • Tutek_MVP4G (wygenerowana klasa domyślnego entryPoint’a)
    Wyżej wymienione rzeczy są zbędne – usuwamy je. Jednak skoro usunęliśmy nasz entry point to jak rozpocznie się aplikacja? następny krok rozwiązuje ten problem – edycja  pliku konfiguracyjnego xml – tutek_MVP4G.gwt.xml :
<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='tutek_mvp4g'>
  <!-- Inherit the core Web Toolkit stuff.                        -->
  <inherits name='com.google.gwt.user.User'/>

  <!-- Inherit the default GWT style sheet.  You can change       -->
  <!-- the theme of your GWT application by uncommenting          -->
  <!-- any one of the following lines.                            -->
  <inherits name='com.google.gwt.user.theme.clean.Clean'/>
  <!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->

  <inherits name='com.mvp4g.Mvp4gModule' />

  <!-- Other module inherits                                      -->

  <!-- Specify the app entry point class.                         -->

  <!-- Specify the paths for translatable code                    -->
  <source path='client'/>
  <source path='shared'/>

</module>

Następnie przystępujemy do tworzenia struktury pakietów projektu. W pakiecie „tutek.client” tworzymy kolejno pakiety „view”, „presenter” oraz „bean”.

    Pora utworzyć pierwszy widok (kolejność nie jest wymuszona) – w pakiecie „tutek.client.view” tworzymy widok – do tego celu wykorzystamy UIBinder. Oczywiście widokiem może być dowolna klasa rozszerzająca Composite, jednak użycie UIBindera nieco więcej nas nauczy na temat mechanizmów MVP4G i tworzenia przez framework widoków oraz wiązania ich z prezenterami.
    Po utworzeniu widoku otwieramy „designera”, jeśli otworzy się okienko projektowania UI wraz z paletą kontrolek to znaczy, że do tej pory nie popełniliśmy błędu, w przeciwnym razie warto sprawdzić czy zostały dodane wszystkie wymagane biblioteki. Kolejnym krokiem jest utworzenie bean’a – będzie to kultowa klasa „Person”. Wiem, wiem – w innych tutorialach mamy przycisk i pole tekstowe. W tym jednak potrzebna jest nam tabelka i przycisk – przyda się do kolejnego wpisu 😉 Wróćmy jednak do tworzenia klasy danych, która powinna wyglądać mniej więcej tak :
package tutek.client.bean;

public class Person
{
 public String name;
 public String surName;

 public Person(String name, String surName)
 {
  super();
  this.name = name;
  this.surName = surName;
 }
}

 

    Wracamy do tworzenia widoku. Dużą zaletą UIBindera jest deklaratywny sposób tworzenia interfejsu z użyciem XML, co doskonale wpasowuje się we wzorzec MVP. Poniżej XML do widoku, wystarczy go skopiować do własnego projektu :

 

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
 xmlns:g="urn:import:com.google.gwt.user.client.ui" xmlns:p1="urn:import:com.google.gwt.user.cellview.client">
 <ui:style>

 </ui:style>
 <g:HTMLPanel>
  <g:VerticalPanel>
   <p1:CellTable ui:field="cellTable"/>
   <g:FlowPanel>
    <g:Button ui:field="btn_filter" text="Filtruj Wacków"/>
   </g:FlowPanel>
  </g:VerticalPanel>
 </g:HTMLPanel>
</ui:UiBinder>

 

 Jednak UIBinder to nie tlyko XML, towarzyszy mu klasa rozszerzająca „com.google.gwt.user.client.ui.Composite”, podobnie jak XML, jest ona generowana podczas pracy z designerem. Jednak wymaga zmian – należy właściwie sparametryzować tabelkę wcześniej stworzoną klasą „Person”.
package tutek.client.view;

import tutek.client.bean.Person;

import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.client.ui.Button;

public class TutekView extends Composite
{

 private static TutekViewUiBinder uiBinder = GWT.create(TutekViewUiBinder.class);

 @UiField(provided=true)
 public CellTable<Person> cellTable = new CellTable<Person>();
 @UiField
 public Button btn_filter;

 interface TutekViewUiBinder extends UiBinder<Widget, TutekView>
 {
 }

 public TutekView()
 {
  initWidget(uiBinder.createAndBindUi(this));
 }

}

Ok, mamy już widok, brakuje teraz prezentera – czegoś co by kontrolowało widok zgodnie z ideą wzorca. Jednak zanim stworzymy klasę rozszerzającą „com.mvp4g.client.presenter.BasePresenter” musimy się zatroszczyć o EventBus – oś wokół której obraca się cały framework MVP4G. Tworzymy zatem interfejs rozszerzający „com.mvp4g.client.event.EventBus”:

package tutek.client;

import com.mvp4g.client.event.EventBus;

public interface TutekEventBus extends EventBus
{
    //pusty interfejs!!
}

Teraz gdy w projekcie mamy obecny eventBus można stworzyć klasę prezentera kontrolującą widok, w skład którego wchodzi tabelka i przycisk. Presenter będzie wypełniał tabelkę danymi oraz definiował obsługę zdarzenia kliknięcia na przycisku.

package tutek.client.presenter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import tutek.client.TutekEventBus;
import tutek.client.bean.Person;
import tutek.client.view.TutekView;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.view.client.ListDataProvider;
import com.mvp4g.client.annotation.Presenter;
import com.mvp4g.client.presenter.BasePresenter;

@Presenter(view = TutekView.class)
public class TutekPresenter extends BasePresenter<TutekView, TutekEventBus>
{
 private ListDataProvider<Person> dataProvider;

Teraz czas na omówienie poszczgólnych fragmentów powyższego kodu. Najpierw za pomocą @Presenter określamy widok który będzie kontrolowany następnie deklarując klasę parametryzujemy ją eventBusem stworzonym w poprzednim kroku oraz widokiem. Następnie przeciążamy metodę „bind” :

 @Override
 public void bind()
 {
  GWT.log("bind");
  //tworzymy kolumny
  TextColumn<Person> name = new TextColumn<Person>()
  {
   @Override
   public String getValue(Person object)
   {
    return object.name;
   }
  };

  TextColumn<Person> surName = new TextColumn<Person>()
  {
   @Override
   public String getValue(Person object)
   {
    return object.surName;
   }
  };

  //dodajemy kolumny
  view.cellTable.addColumn(name);
  view.cellTable.addColumn(surName);

  //obsługa zdarzeń
  view.btn_filter.addClickHandler(new ClickHandler()
  {
   @Override
   public void onClick(ClickEvent event)
   {
    List<Person> lst = dataProvider.getList();
    List<Person> newLst = new ArrayList<Person>();
    for(Person prs : lst)
    {
     if(prs.name.toLowerCase().equals("wacek"))
     {
      newLst.add(prs);
     }
    }
    dataProvider.setList(newLst);
   }
  });
 }

Metoda „bind” jest wywoływana przy pierwszym odwołaniu do prezentera – jest to dobre miejsce na zainicjowanie kontrolek. Najpierw tworzymy kolumny dla tabeli, dodajemy je oraz na koniec definiujemy obsługę zdarzeń – przefiltrowanie wszystkich Wacków, mało eleganckie – ale zaradzimy temu w kolejnym poście ;). Pora wrócić do EventBus i uzupełnić interfejs o anotacje oraz zdarzenia :

package tutek.client;

import tutek.client.presenter.TutekPresenter;
import com.mvp4g.client.annotation.Event;
import com.mvp4g.client.annotation.Events;
import com.mvp4g.client.annotation.Start;
import com.mvp4g.client.event.EventBus;

@Events(startPresenter = TutekPresenter.class)
public interface TutekEventBus extends EventBus
{

 @Start //obowiązkowe zdarzenie startowe
 @Event(handlers = {TutekPresenter.class})
 public void onStart();

}

Trochę kodu przybyło. Pierwszą sprawą jest określenie inicjalnego prezentera – stanu początkowego. Następnie definiujemy zdarzenia zachodzące w aplikacji. Zdarzenie to nic innego jak metoda interfejsu. Wśród zdarzeń musi być wyszczególnione anotacją @Start zdarzenie startowe – łatwo zapamiętać- tylko jedno zdarzenie może posiadać tą anotację, która z koleji musi wystąpić. Dla każdego zdarzenia obowiązkowa jest anotacja @Event i to ona stanowi, że metoda interfejsu jest zdarzeniem. W parametrach tej anotacji określamy obsługujące prezentery – w tym przypadku tylko jeden, w którym musi być metoda obsługująca to zdarzenie o nazwie onOnStart(). Zasada jest prosta, trochę jak z java beans, do nazwy metody z iterfejsu EventBus dodajemy przedrostek „on” a pierwszą literę pierwotnej nazwy zmieniamy na wielką. Pora wrócić do prezentera i zobaczyć jak wygląda obsługa zdarzenia :

 public void onOnStart()
 {
  GWT.log("onStart()");
  //tworzymy dane
  Person persons[] = new Person[]{new Person("Wacek", "Kowalski"), new Person("Wacek", "Siczek"), new Person("Marcel", "Plusk"),
   new Person("Bronek", "Ogonek"), new Person("Wacek", "wachowski")};

  dataProvider = new ListDataProvider<Person>();
  dataProvider.addDataDisplay(view.cellTable);

  dataProvider.setList( new ArrayList<Person>(Arrays.asList(persons) ) );
 }
}

Wywołanie metody „onOnStart” następuje po wygenerowaniu zdarzenia – odpowiada za to anotacja @Start, następuje po wywołaniu metody „bind” podczas inicjalizacji prezentera. Pozostałe zdarzenia są inicjowane w prezentererze za pomocą wywołania eventBus.<zdarzenie> – w tym momencie jest generowane zdarzenie, trafia na szynę gdzie rozjeżdża (obrazowo ;)) je prezenter zadeklarowany w parametrze „handlers” anotacji @Event . Wracając do naszego zdarzenia, jako że jest to zdarzenie generowane automatycznie w ramach inicjalizacji całej aplikacji, wypełniamy tabelkę danymi.

To już wszystko. Pozostaje jedynie uruchomić projekt. Jak widać korzystanie z MVP4G nie jest ani trudne, ani nie generuje kodu tempie wykładniczym – wręcz przeciwnie. Gotowy projekt Eclipsa również umieściłem   w moim magazynku plików do ściągnięcia. Następny wpis będzie rozwinięciem tego co teraz zostało zrobione.

Tags: , , , , , , , ,
Napisany w Java, WWW | Brak komentarzy »

Inne ciekawostki Google’a

Autor: Piotr Karpiuk o 29. czerwca 2011

Masz w systemie plików obrazek i chciałbyś znaleźć podobne w Internecie? W wyszukiwarce Google’a wejdź na zakładkę Grafika i przeciągnij obrazek z pulpitu lub managera plików na pole wyszukiwania w wyszukiwarce.

Program GmailDrive Shell Extension postrzega udostępnianą przez Google’a 7GB przestrzeń w chmurze jak wirtualny dysk pod Windowsami. Największy plik jaki możemy wgrać nie może być większy niż 25MB. Aby korzystać z nowej partycji, po pierwszym podwójnym kliknięciu w ikonę dysku w systemie plików wpisujemy nasz login i hasło do Gmaila.

Na stronie image-swirl.googlelabs.com jest dość specyficzna wyszukiwarka Google’a; po wyszukaniu obrazka pasującego do frazy i kliknięciu go, wyświetli się diagram zawierający w każdej z gałęzi podobne grafiki.

Na stronie labs.google.com/sets po wpisaniu słowa lub paru słów Google spróbuje podać inne słowa kojarzące się z nim lub należące do tej samej grupy rodzajowej.

Ciekawostki:

  • Google traci rocznie ponad 100mln dolarów z tytułu opłat za reklamy przez to, że użytkownicy klikają na przycisk „Szczęśliwy traf”, przechodząc od razu do strony losowo wybranej z listy wyników wyszukiwania.
  • Na początku działalności firmy dyski z danymi umieszczone były w szafie zbudowanej z klocków LEGO.
  • „Czego nie ma w Google, tego nie ma w ogóle.” – ot, powiedzonko takie.

Patrz także Wszystko o Google – tutaj Google zwięźle informuje co ma do zaoferowania użytkownikom i webmasterom, link do bloga firmy, sklepu z gadżetami itp.

Tags:
Napisany w WWW | Brak komentarzy »

Co wie o Tobie Google?

Autor: Piotr Karpiuk o 30. maja 2011

Masz w Google’u swoje konto? W takim razie – o ile się tym dotąd nie zainteresowałeś – Google automatycznie notuje wszystkie frazy które wpisałeś w wyszukiwarce i udostępnia Ci je do wglądu i przeszukiwania, co może być równie przydatne co demaskujące gdyby trafiło w niewłaściwe ręce. Zajrzyj na stronę http://www.google.com/history, a możesz się zdziwić. Na szczęście możesz też wstrzymać śledzenie a nawet usunąć historię.

Na stronie http://www.google.com/dashboard dowiesz się jakie informacje o Tobie jako osobie prywatnej przechowuje Google na swoich serwerach, a nawet będziesz miał okazję połapać się z jakich usług Google’a zdarzyło Ci się już korzystać.

Polecam również zbiór ciekawostek na temat bezpieczeństwa na Facebooku oraz krótki ale treściwy artykuł o bezpieczeństwie Windows.

Tags: , ,
Napisany w WWW | Brak komentarzy »