<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Xoft</title>
	<atom:link href="http://www.xoft.pl/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.xoft.pl</link>
	<description>O programowaniu i innych rzeczach</description>
	<lastBuildDate>Fri, 12 Feb 2010 14:42:55 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Java FX na Olimpiadzie zimowej</title>
		<link>http://www.xoft.pl/2010/02/12/java-fx-na-olimpiadzie-zimowej/</link>
		<comments>http://www.xoft.pl/2010/02/12/java-fx-na-olimpiadzie-zimowej/#comments</comments>
		<pubDate>Fri, 12 Feb 2010 14:39:30 +0000</pubDate>
		<dc:creator>Piotr Kochański</dc:creator>
				<category><![CDATA[JAVA]]></category>
		<category><![CDATA[JAVAFX]]></category>
		<category><![CDATA[RIA]]></category>

		<guid isPermaLink="false">http://www.xoft.pl/?p=186</guid>
		<description><![CDATA[Jeszcze się okaże, że będę musiał zmienić swoje sceptyczne nastawienie do JavaFX. JavaFX zostało wybrane jako technologia do wizualizacji danych o medalach na stronie Olimpiady Vancouver 2010.
Wygląda toto faktycznie dobrze, nie da się odróżnić od Flasha, można złośliwie powiedzieć. Jak Oracz zainwestuje w poprawienie narzędzi deweloperskich i sensowną bibliotekę komponentów, to może coś z tego [...]]]></description>
			<content:encoded><![CDATA[<p>Jeszcze się okaże, że będę musiał zmienić swoje sceptyczne nastawienie do <a href="http://javafx.com/">JavaFX</a>. JavaFX zostało wybrane jako technologia <a href="http://www.vancouver2010.com/olympic-medals/geo-view/">do wizualizacji danych o medalach</a> na stronie Olimpiady Vancouver 2010.</p>
<p>Wygląda toto faktycznie dobrze, nie da się odróżnić od Flasha, można złośliwie powiedzieć. Jak Oracz zainwestuje w poprawienie narzędzi deweloperskich i sensowną bibliotekę komponentów, to może coś z tego będzie.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xoft.pl/2010/02/12/java-fx-na-olimpiadzie-zimowej/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Extreme Programming w San Diego</title>
		<link>http://www.xoft.pl/2010/02/12/extreme-programming-w-san-diego/</link>
		<comments>http://www.xoft.pl/2010/02/12/extreme-programming-w-san-diego/#comments</comments>
		<pubDate>Fri, 12 Feb 2010 14:30:32 +0000</pubDate>
		<dc:creator>Piotr Kochański</dc:creator>
				<category><![CDATA[JAVA]]></category>

		<guid isPermaLink="false">http://www.xoft.pl/?p=181</guid>
		<description><![CDATA[Ubawił mnie niezwykle wpis Bruce Eckela na Artima.com:
Last night I went to the Extreme Programming San Diego meeting (it should probably be called &#8220;Agile&#8221; but I suspect the group is old enough that XP was what started it). A reader knew that I was in San Diego visiting my folks so he suggested it. I [...]]]></description>
			<content:encoded><![CDATA[<p>Ubawił mnie niezwykle <a href="http://www.artima.com/weblogs/viewpost.jsp?thread=281754">wpis</a> Bruce Eckela na Artima.com:</p>
<blockquote><p>Last night I went to the Extreme Programming San Diego meeting (it should probably be called &#8220;Agile&#8221; but I suspect the group is old enough that XP was what started it). A reader knew that I was in San Diego visiting my folks so he suggested it. I thought I&#8217;d dip my toe in the water and see if programming was any more interesting to me than the last time I checked. Especially since Agile practices tend to be more focused on people issues, which is where I seem to continue to drift.</p>
<p>The first thing I noticed was the turnout. I&#8217;m guessing there are thousands of programmers in San Diego, <strong>but we got maybe 10 people to show up</strong>.</p></blockquote>
<p>Chyba w San Diego już się połapali, że 90% Agile to wynalazek cwanych konsultantów&#8230; 10 osób to i tak niezły wynik.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xoft.pl/2010/02/12/extreme-programming-w-san-diego/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sun w łapach Oracla</title>
		<link>http://www.xoft.pl/2010/02/02/sun-w-lapach-oracla/</link>
		<comments>http://www.xoft.pl/2010/02/02/sun-w-lapach-oracla/#comments</comments>
		<pubDate>Mon, 01 Feb 2010 23:06:53 +0000</pubDate>
		<dc:creator>Piotr Kochański</dc:creator>
				<category><![CDATA[JAVA]]></category>

		<guid isPermaLink="false">http://www.xoft.pl/?p=173</guid>
		<description><![CDATA[No i stało się, firma Sun przeszła do historii. Oracle szumnie zapowiada, jak to będzie rozwijał i inwestował. Zapowiedzi mają niewielkie znaczenie, nawet pierwszy rzut oka wyglądają pięknie, ale parę nieprzyjemnych rzeczy z nich wynika. Nieprzyjemnych dla użytkowników produktów SUN-a.
Weźmy NetBeans. Oracle ogłosił:
Oracle will invest in the NetBeans IDE and NetBeans.org community, but it will [...]]]></description>
			<content:encoded><![CDATA[<p>No i stało się, firma Sun przeszła do historii. Oracle szumnie zapowiada, jak to będzie rozwijał i inwestował. Zapowiedzi mają niewielkie znaczenie, nawet pierwszy rzut oka wyglądają pięknie, ale parę nieprzyjemnych rzeczy z nich wynika. Nieprzyjemnych dla użytkowników produktów SUN-a.</p>
<p>Weźmy NetBeans. Oracle ogłosił:</p>
<blockquote><p>Oracle will invest in the NetBeans IDE and NetBeans.org community, but it will be limited to Java Standard Edition, scripting languages, mobile Java, JavaFX and Solaris. Oracle&#8217;s own Java IDE, JDeveloper IDE, will be Oracle&#8217;s enterprise application development tool.</p></blockquote>
<p>Czyli NetBeans będzie środowiskiem programistycznym do wszystkiego, tylko nie do Java EE. Ponieważ gro aplikacji Javowych jest pisanych właśnie w Java EE, to praktycznie oznacza utrupienie NetBeans na rzecz JDevelopera. Kto będzie chciał się wozić ze środowiskiem Javowym, w którym nie można napisać aplikacji JEE? Szkoda.</p>
<p>GlassFish ma być wspierany i rozwijany, ale jako serwer do aplikacji intranetowych:</p>
<blockquote><p>WebLogic will be sold as the company&#8217;s enterprise application server, while the free and open source Glassfish server will be Oracle&#8217;s department-level application server.
</p></blockquote>
<p>Myślałem, że Oracle będzie chciał podgryzać GlassFishem JBoss-a, ale wygląda na to, że nie. W takim razie, kto będzie chciał się bawić w serwer, który w założeniu ma być &#8220;gorszy&#8221; od WebLogic-a. GlassFish, czyli tak na prawdę Sun GlassFish Enterprise Server (dawniej Sun ONE Application Server), jakoś szczególnie nie ustępuje WebLogicowi, co więc zamierza Oracle? Ma już dwa serwery aplikacyjne, teraz dochodzi trzeci; obawiam się, że GlassFish może szybo odejść w odstawkę pod pretekstem nie przynoszenia zysków. Chyba, że faktycznie Oracle ma jakiś dobry pomysł na niego.</p>
<p>Wygląda na to, że z kolei Oracle zamierza inwestować w JavaFX. To akurat mocno mnie dziwi, bo póki co na tle Silverlighta czy Flex-a JavaFX nie wygląda najlepiej, przynajmniej od strony potencjału bycia czymś równie popularnym, jak konkurenci. Technologicznie JavaFX jest bardzo fajnym pomysłem, ale funkcjonalność, dostępne komponenty są lata świetlne za innymi wynalazkami. Może tutaj Oracle ma jakiś ciekawy pomysł na biznes?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xoft.pl/2010/02/02/sun-w-lapach-oracla/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Wstrzykiwanie zależności czyli Dependency Injection. Część 5: wykorzystanie Google Guice</title>
		<link>http://www.xoft.pl/2009/07/09/wstrzykiwanie-zaleznosci-czyli-dependency-injection-czesc-5-wykorzystanie-google-guice/</link>
		<comments>http://www.xoft.pl/2009/07/09/wstrzykiwanie-zaleznosci-czyli-dependency-injection-czesc-5-wykorzystanie-google-guice/#comments</comments>
		<pubDate>Thu, 09 Jul 2009 06:24:34 +0000</pubDate>
		<dc:creator>Piotr Kochański</dc:creator>
				<category><![CDATA[DEPENDENCY INJECTION]]></category>
		<category><![CDATA[GUICE]]></category>
		<category><![CDATA[JAVA]]></category>
		<category><![CDATA[GOOGLE GUICE]]></category>
		<category><![CDATA[WSTRZYKIWANIE ZALEŻNOŚCI]]></category>

		<guid isPermaLink="false">http://www.xoft.pl/?p=151</guid>
		<description><![CDATA[W poprzednim odcinku widzieliśmy, jak można sobie ułatwić życie wstrzykując zależności do klas przy pomocy Springa [reszta cyklu:  część 1., część 2., część 3.]. Dzisiaj czas na coś prostszego i przyjemniejszego: Google Guice!

Przypomnijmy sobie jaki problem rozwiązujemy. Otóż mamy klasę NewsService, która potrzebuje do działania dwóch innych klas, które są konkretnymi implementacjami interfejsów Authenticator [...]]]></description>
			<content:encoded><![CDATA[<p>W <a href="/2009/04/30/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-4-wykorzystanie-spring-a/">poprzednim odcinku</a> widzieliśmy, jak można sobie ułatwić życie wstrzykując zależności do klas przy pomocy Springa [reszta cyklu:  <a href="http://www.xoft.pl/2008/12/23/depedency-injection-spring-intro-baddesing">część 1.</a>, <a href="http://www.xoft.pl/2008/12/28/depedency-injection-spring-intro">część 2.</a>, <a href="http://www.xoft.pl/2009/04/10/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-3-jak-zle-uzywac-di">część 3.</a>]. Dzisiaj czas na coś prostszego i przyjemniejszego: <a href="http://code.google.com/p/google-guice">Google Guice</a>!<br />
<span id="more-151"></span><br />
Przypomnijmy sobie jaki problem rozwiązujemy. Otóż mamy klasę <code>NewsService</code>, która potrzebuje do działania dwóch innych klas, które są konkretnymi implementacjami interfejsów <code>Authenticator</code> oraz <code>Storage</code>. Dodatkowo <code>Storage</code> potrzebuje implementacji klasy<code> Driver</code>. </p>
<p>Dzięki temu, że <code>NewsService</code> zależy od interfejsów, a nie klas konkretnych możemy wstrzykiwać mu taką implementację danego interfejsu, jaka jest potrzebna w określonej sytuacji. Jak stwierdziliśmy, ręczne wstrzykiwanie na dłuższą metę jest męczące, w związku z tym postanowiliśmy użyć do tego celu Spring-a. Działa on w ten sposób, że w pliku konfiguracyjnym określamy zależności między klasami, które potem Spring już samodzielnie wstrzykuje we wskazana miejsce.</p>
<p>Podejście użyte przez Spring-a ma parę wad. Po pierwsze jest XML-owy plik konfiguracyjny, który musi być zsynchronizowany z kodem. Można w nim zrobić pomyłkę (dobre IDE pomaga unikać błędów), refaktoryzacja kodu jest utrudniona &#8211; dobre IDE pozwala w miarę niezawodnie przeprowadzać tę operację, ale czasem i ono może pomylić się, w końcu XML to tylko łańcuchy znaków. Wreszcie do pliku Springa czasem trzeba dodać więcej informacji, niż to jest tak na prawdę potrzebne (wersja 2.0 ograniczyła ten problem).</p>
<p>Czy można zrobić kontener DI lepiej? Narzuca się oczywiście użycie metadanych (ang. <em>annotations</em>), w końcu od Java 5.0 jest to nowa, skuteczna metoda kontroli zachowania aplikacji w określonym środowisku.</p>
<p>Warto się zastanowić dwa razy nad użyciem tego mechanizmu, bo zewnętrzna konfiguracja wcale nie jest taką złą rzeczą. Na przykład kwerendy nazwane dla JPA zdecydowanie wygodniej jest definiować w pliku, mimo, że można użyć metadanych. </p>
<p>Zobaczymy jednak, że Google Guice oferuje nam rozwiązanie, bazujące na wykorzystaniu metadanych, które faktycznie jest wygodniejsze w użyciu niż Spring. </p>
<p>Jak działa Guice? W kodzie umieszczamy metadane, które mówią gdzie potrzebujemy wstrzyknięcia zależności. Zajmuje się tym metadana <code>@Inject</code>.</p>
<p>Samo powiedzenie, że nasza klasa potrzebuje wstrzyknięcia instancji innej klasy to jeszcze za mało. Musimy powiedzieć o jaką konkretną implementację nam chodzi. W przypadku Spring-a ta informacja była zaszyta w konfiguracji w pliku XML. Google Guice podchodzi do tego tematu inaczej. Definicje zależności między klasami umieszczamy w osobnej klasie Java &#8211; module Guice. Dzięki temu definicje te są odporne na literówki i poddają się bezboleśnie refaktoryzacji.</p>
<p>Zobaczmy jak to wygląda w praktyce. Pierwsza rzecz, to trzeba <a href="http://code.google.com/p/google-guice">pobrać</a> Google Guice i dołączyć zawarte w ściągniętej paczce pliki JAR do ścieżki swojego projektu.</p>
<p>Gdy mamy już skonfigurowany do pracy projekt jesteśmy gotowi zacząć pracę z Guice. Przede wszystkim musimy zmodyfikować klasę NewsService:</p>
<pre class="brush: java;">
import com.google.inject.Inject;
import pl.erudis.newsservice.di.storage.Storage;
import pl.erudis.newsservice.di.auth.Authenticator;

public class NewsService {

  Storage storage;
  Authenticator authenticator;

  public NewsService() {
  }

  public NewsService(Storage storage) {
    this.storage = storage;
  }

  public Authenticator getAuthenticator() {
    return authenticator;
  }

  @Inject
  public void setAuthenticator(Authenticator authenticator) {
    this.authenticator = authenticator;
  }

  public Storage getStorage() {
    return storage;
  }

  @Inject
  public void setStorage(Storage storage) {
    this.storage = storage;
  }

  public void addNews(String news){
    if(authenticator != null){
      authenticator.authenticate();
    }
    storage.save(news.getBytes());
    System.out.println("News saved...");
  }
}
</pre>
<p>Klasa ta w zasadzie nie zmieniła się w stosunku do wersji używanej ze Springiem, czy w ogóle bez kontenera DI, jedyna rzecz, która się pojawiła, to metadana <code>@Inject</code> w tych miejscach, gdzie potrzebne jest nam wstrzyknięcie instancji odpowiedniej klasy.</p>
<p>Jest to bardzo ważna rzecz: użycie Guice tak jak Springa nie wymaga zmian w kodzie &#8211; metadane to nie jest tak na prawdę zmiana kodu, tylko szczególny sposób konfiguracji aplikacji na potrzeby jakiegoś narzędzia.</p>
<p>Podobnie musimy zmodyfikować klasę DBStorage:</p>
<pre class="brush: java;">
import com.google.inject.Inject;
import pl.erudis.newsservice.di.drivers.Driver;

public class DBStorage implements Storage{

  Driver driver;

  @Inject
  public DBStorage(Driver driver) {
    this.driver = driver;
  }

  public void save(byte[] data) {
    driver.openConnection();
    System.out.println("Saving in database: '" + new String(data) +"'");
    driver.closeConnection();
  }

}
</pre>
<p>Teraz pozostaje tylko powiedzieć co ma być wstrzykiwane w oznaczone metadaną <code>@Inject</code> miejsca. W tym celu definiujemy moduł:</p>
<pre class="brush: java;">
import com.google.inject.Binder;
import com.google.inject.Module;
import pl.erudis.newsservice.di.auth.Authenticator;
import pl.erudis.newsservice.di.auth.UsernamePassAuthenticator;
import pl.erudis.newsservice.di.drivers.Driver;
import pl.erudis.newsservice.di.drivers.SqlDbDriver;
import pl.erudis.newsservice.di.storage.DBStorage;
import pl.erudis.newsservice.di.storage.Storage;

public class NewsServiceModule implements Module{

  public void configure(Binder binder) {
    binder.bind(Authenticator.class).to(UsernamePassAuthenticator.class);
    binder.bind(Driver.class).to(SqlDbDriver.class);
    binder.bind(Storage.class).to(DBStorage.class);
  }
}
</pre>
<p>Moduł wiąże ze sobą interfejsy, których używamy w kodzie z konkretnymi implementacjami, wybranymi przez nas w danej sytuacji. Warto zwrócić uwagę na bardzo inteligentnie wybrane nazwy metod. Kod czyta się jak narrację, w końcu język programowania ma być językiem. Trudno się nie połapać co oznacza kod &#8220;powiąż Authenticator.class z UsernamePassAuthenticator.class&#8221;.</p>
<p>Widać też od razu, że bardzo łatwo dynamicznie tworzyć powiązania &#8211; wszystko robimy w kodzie Java.<br />
Wreszcie ostatni element, czyli klasa kliencka</p>
<pre class="brush: java;">
import com.google.inject.Guice;
import com.google.inject.Injector;

public class GuiceClient {
  public static void main(String[] args) {
    Injector i = Guice.createInjector(new NewsServiceModule());
    NewsService newsService = i.getInstance(NewsService.class);
    newsService.getAuthenticator().login(new String[]{"beer", "beer"});
    newsService.addNews("bla bla guice bla");
  }
}
</pre>
<p>Zarówno klasa kliencka jak i definicja modułów mają bardzo istotną zaletę w stosunku do Spring-a &#8211; nigdzie nie występują łańcuchy znaków reprezentujące klasy. Używały wyłącznie kompilowalnych artefaktów, dzięki czemu automatycznie odpada nam cała klasa przykrych błędów.</p>
<p>Jeżeli ktoś interesuje się działaniem Spring Framework, to pewnie wie, że od pewnego czasu także Spring może być konfigurowany przy pomocy metadanych, są jednak bardzo zasadnicze różnice pomiędzy sposobem robienia tego przez Spring-a i przez Guice, o tym będziemy mówić w kolejnym odcinku naszej serii.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xoft.pl/2009/07/09/wstrzykiwanie-zaleznosci-czyli-dependency-injection-czesc-5-wykorzystanie-google-guice/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Wstrzykiwanie zależności czyli Dependency Injection w 9 minut i 59 sekund. Część 4: wykorzystanie Spring-a</title>
		<link>http://www.xoft.pl/2009/04/30/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-4-wykorzystanie-spring-a/</link>
		<comments>http://www.xoft.pl/2009/04/30/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-4-wykorzystanie-spring-a/#comments</comments>
		<pubDate>Thu, 30 Apr 2009 13:36:24 +0000</pubDate>
		<dc:creator>Piotr Kochański</dc:creator>
				<category><![CDATA[DEPENDENCY INJECTION]]></category>
		<category><![CDATA[JAVA]]></category>
		<category><![CDATA[SPRING]]></category>

		<guid isPermaLink="false">http://www.xoft.pl/?p=98</guid>
		<description><![CDATA[Jak widzieliśmy  [część 1., część 2., część 3.] wstrzykiwanie zależności jest całkiem sympatycznym pomysłem (o ile się go poprawnie używa), ale można zapytać się, na ile jest to kosztowne? Przez koszty rozumiem tutaj nakład pracy, jaki trzeba włożyć w używanie architektury wykorzystującej DI.
Wyobraźmy sobie, że nasza aplikacja często wykorzystuje klasę NewsService.  Co to [...]]]></description>
			<content:encoded><![CDATA[<p>Jak widzieliśmy  [<a href="http://www.xoft.pl/2008/12/23/depedency-injection-spring-intro-baddesing">część 1.</a>, <a href="http://www.xoft.pl/2008/12/28/depedency-injection-spring-intro">część 2.</a>, <a href="http://www.xoft.pl/2009/04/10/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-3-jak-zle-uzywac-di">część 3.</a>] wstrzykiwanie zależności jest całkiem sympatycznym pomysłem (o ile się go poprawnie używa), ale można zapytać się, na ile jest to kosztowne? Przez koszty rozumiem tutaj nakład pracy, jaki trzeba włożyć w używanie architektury wykorzystującej DI.</p>
<p>Wyobraźmy sobie, że nasza aplikacja często wykorzystuje klasę <a href="http://www.xoft.pl/2008/12/28/depedency-injection-spring-intro/#listing.NewsService">NewsService</a>.  Co to oznacza w praktyce? Za każdym razem musimy wstrzykiwać do niej zależność, których potrzebuje. Nasz przykład jest dość prosty, zależności nie ma zbyt wiele, ale nawet tutaj powtarzanie przy każdym użyciu kodu</p>
<pre class="brush: java;">
public class Client {
  public static void main(String[] args) {
    Authenticator authenticator = new UsernamePassAuthenticator("beer","beer");
    Driver driver = new SqlDbDriver();

    //inject dependency #1
    Storage storage = new DBStorage(driver);
    //inject dependency #2
    NewsService newsService = new NewsService(storage);
    //inject dependency #3
    newsService.setAuthenticator(authenticator);

    newsService.addNews("ble ble ble");
  }
}
</pre>
<p>nie jest zbyt zachęcające. Czy nie lepiej jest użyć jednak jakiego wzorca <em>factory</em> czy <em>service locator</em>, żeby jednak <code>NewsService </code> sam sobie znalazł potrzebne klasy?<br />
<span id="more-98"></span><br />
Okazuje się, że problem można rozwiązać bez rezygnowania z DI wykorzystując <em>kontenera Dependency Injection</em>, taki jak <a href="http://www.springsource.org" target="_blank">Spring</a>, <a href="http://www.picocontainer.org">PicoContainer</a> czy <a href="http://code.google.com/p/google-guice">Google Guice</a>.</p>
<p>Cóż takiego nadzwyczajnego robią te kontenery, że ludzie ich używają. Ich działanie tak na prawdę sprowadza się do tego, że pozwalają w jakiś sposób w jednym miejscu powiedzieć, że dana klasa potrzebuje konkretnej implementacji interfejsu lub interfejsów.</p>
<p>W naszym przykładzie chcemy wskazać, że <code>NewsService</code> potrzebuje klasy <code>DBStorage</code>, która implementuje <code>Storage</code>, a klasa <code>DBStorage</code> potrzebuje klasy <code>SqlDbDriver</code>, implementującej interfejs <code>Driver</code>.</p>
<p>Kontenery różnią się od siebie sposobem definiowania zależności między klasami. Spring pozwala konfigurować te zależności w pliku XML-owym lub (od niedawna) przy pomocy metadanych (ang. <em>annotations</em>). Google Guice jest nowszym rozwiązaniem i bazuje na wykorzystaniu metadanych.</p>
<p>Można, rzecz jasna, napisać własnych &#8220;kontener DI&#8221;, czyli pojedynczą klasę, w której będzie zawarta informacją o zależnościach między klasami, ale to oznacza dodatkową pracę, zastanawianie się na ile nasz pomysł na kontener będzie uniwersalny, itp.</p>
<p>Zaletą centralnej konfiguracji zależności jest możliwość ich podmieniania w razie potrzeby. Na przykład na potrzeby testów możemy jako implementacji <code>Storage</code> używać jakiejś naszej klasy, która udaje <code>Storage</code> na tyle, na ile jest to potrzebne do testów jednostkowych czy funkcjonalnych. Dzięki temu testy wykonują się szybciej, nie jest potrzebna baza danych.</p>
<p>Dodatkowo, jeżeli klient zażyczy sobie, żeby dane były przechowywane w plikach tekstowych, a nie bazie danych, to wystarczy, że napiszemy odpowiednie implementację <code>Storage</code> i/lub <code>Driver</code> i zmienimy konfigurację wstrzykiwania zależności w ten sposób, żeby były użyte nowe implementacje.</p>
<p>Oczywiście nie ma nic za darmo, to my musimy utrzymywać plik konfiguracyjny, który potrafi być całkiem pokaźnych rozmiarów w większej aplikacji lub pilnować aktualizacji parametrów odpowiednich metadanych. </p>
<p>Swoją drogą jest jedna technologia, w której mamy do dyspozycji DI, a nie musimy sami nic konfigurować &#8211; to te paskudne EJB, przez wielu szczerze znienawidzone. Tam po prostu serwer aplikacji przechowuje informację o komponentach, które mają być udostępniane w ramach aplikacji i jeżeli chcemy jakiegoś komponentu użyć, to po prostu odwołujemy się do niego poprzez interfejs, a serwer sam nam wstrzykuje odpowiedni obiekt:</p>
<pre class="brush: java">
@Stateless
public class PierwszyBean implements PierwszyBeanInterfejs {
  //implementację DrugiBean wstrzyknie sam serwer aplikacji
  @EJB DrugiBeanInterfejs drugiBean;

  public void metoda(){
    drugiBean.zrobCos();
  }
}
</pre>
<p>Nie mamy żadnego pliku konfiguracyjnego, w którym byśmy mogli godzinami szukać źle zdefiniowanych zależności ;).</p>
<p>Ok, tak na serio pilnowanie zależności nie jest aż tak bardzo straszne. Użycie kontenerów zazwyczaj jednak ułatwia życie, zobaczmy więc jak działa najpopularniejszy z nich: Spring.</p>
<p>Pierwsza rzecz, to musimy sobie ściągnąć ze strony http://www.springsource.org/ Spring Framework. Ja używałem wersji 2.5.X. </p>
<p>Prościej sprawę można załatwić używając rozsądnego IDE, które ma wbudowane wsparcie dla Spring-a. W paczce dostajemy to razem z NetBeans, MyEclipse IDE, IntelliJ Idea. Jak ktoś używa Eclipse, to można sobie doinstalować odpowiednią wtyczkę. Użycie IDE ma głęboki sens, bo dostajemy podpowiadanie składni w XML-owym pliku konfiguracyjnym, a czasem nawet wizualne narzędzie, które pozawala łatwiej oglądać zależności.</p>
<p>Zakładamy zatem, że mamy na ścieżce klas dodane biblioteki Spring-a.</p>
<p>Co teraz? Czy musimy jakoś modyfikować klasy które już napisaliśmy? Na szczęście nie, nasza aplikacja używa już DI, więc nawet linijki kodu ruszyć nie musimy. Zrobić musimy dwie rzeczy. Po pierwsze musimy zadeklarować zależności między klasami. Po drugie musimy zrobić użytek z tej konfiguracji modyfikując odpowiednio klasę <code>Client</code>, która używa <code>NewsService</code>. </p>
<p>Zacznijmy od konfiguracji. Plik może mieć dowolną nazwę, tutaj jest to <em>springXMLConfig.xml</em>, a jego zawartość widzimy na poniższym listingu:</p>
<pre name="code" class="xml">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd&quot;&gt;

  &lt;bean name=&quot;sqldriver&quot; class=&quot;pl.erudis.newsservice.di.drivers.SqlDbDriver&quot;/&gt;

  &lt;bean name=&quot;dbstorage&quot; class=&quot;pl.erudis.newsservice.di.storage.DBStorage&quot;&gt;
    &lt;constructor-arg ref=&quot;sqldriver&quot;/&gt;
  &lt;/bean&gt;

  &lt;bean name=&quot;upauth&quot; class=&quot;pl.erudis.newsservice.di.auth.UsernamePassAuthenticator&quot; /&gt;

  &lt;bean name=&quot;newsService&quot; class=&quot;pl.erudis.newsservice.di.NewsService&quot; &gt;
    &lt;property name=&quot;authenticator&quot; ref=&quot;upauth&quot;/&gt;
    &lt;property name=&quot;storage&quot; ref=&quot;dbstorage&quot;/&gt;
  &lt;/bean&gt;
&lt;/beans&gt;
</pre>
<p>Co się tutaj dzieje. Na początku deklarujemy użycie klasy <code>SqlDbDriver</code>. Podobnie deklarujemy użycie klasy <code>DBStorage</code>, dodatkowo zaznaczamy, że klasa <code>DBStorage</code> będzie używała klasy <code>SqlDbDriver</code>. Odpowiada za to element</p>
<pre class="brush: xml">
<constructor-arg ref="sqldriver"/>
</pre>
<p>Mówi on Springowi, żeby przy inicjalizacji klasy <code>DBStorage</code> do konstruktora &#8220;wstrzyknął&#8221; obiekt wskazanej przez nas klasy, czyli <code>SqlDbDriver</code>.</p>
<p>Dalej wszystko robimy analogicznie, deklarujemy użycie klasy <code>UsernamePassAuthenticator</code>, a następnie informujemy Spring, że <code>NewsService</code> potrzebuje klas <code>DBStorage</code> oraz <code>UsernamePassAuthenticator</code>. W tym przypadku obiekty klas nie są przekazywane jako parametry konstruktora, tylko inicjalizują pola klasy <code>NewsService</code>, używając odpowiednich metod &#8220;set&#8221; dla pól.</p>
<p>Wreszcie nadszedł czas na nagrodę, czyli wykorzystanie Springa do zarządzania zależnościami, zobaczmy jak wygląda teraz klasa kliencka:</p>
<pre class="brush: java">
package pl.erudis.newsservice.di;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 *
 * @author Piotr Kochański, www.xoft.pl
 */
public class SpringClient {
  public static void main(String[] args) {
    ApplicationContext factory = new ClassPathXmlApplicationContext("META-INF/springXMLConfig.xml");
    NewsService newsService = (NewsService)factory.getBean("newsService");
    newsService.getAuthenticator().login(new String[]{"beer", "beer"});
    newsService.addNews("bla bla spring bla");
  }
}
</pre>
<p>Pierwszym krokiem jest inicjalizacja Springa, jest to robione przy użyciu klasy <code>ApplicationContext</code> &#8211; musimy po prostu powiedzieć Springowi, żeby wczytał z pliku konfiguracyjnego zestaw zależności. Zgodnie z często używaną konwencją plik konfiguracyjny Springa jest w katalogu META-INF, który muszą &#8220;widzieć&#8221; skompilowane klasy Java.</p>
<p>Po inicjalizacji zostaje już nie wiele do zrobienia, wyciągamy klasę, która nas interesuje, czyli <code>NewsService</code> przy pomocy <code>ApplicationContext</code> przekazując mu nazwę tej klasy skonfigurowaną w <em>springXMLConfig.xml</em>. Spring automatycznie przekaże do <code>NewsService</code> potrzebne zależności i dalej używamy jej zapominając o DI czy Springu.</p>
<p>Zaleta wykorzystania kontenera DI jest jasna &#8211; mamy spójny sposób deklarowania zależności między komponentami, dzięki czemu pisząc kod możemy się koncentrować na jego faktycznej funkcjonalności, a nie pilnować odpowiednich związków z innymi klasami.</p>
<p>Oczywiście Spring nie jest tylko samym kontenerem DI, potrafi znacznie więcej rzeczy, ale tutaj nas interesował tylko ten jego aspekt.</p>
<p>Źródła przykładowej aplikacji można ściągnąć klikając <a href="/wp-content/uploads/2009/04/newsservice-src.zip">tutaj</a>, jeżeli ktoś woli, to jest także do pobrania <a href="/wp-content/uploads/2009/04/newsservice-netbeans.zip">projekt NetBeans-a</a> w wersji 6.5, który zawiera tę aplikację.</p>
<p>W kolejnej części cyklu zobaczymy w jaki sposób użyć kontenera Google Guice do zarządzania zależnościami w DI.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xoft.pl/2009/04/30/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-4-wykorzystanie-spring-a/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Wstrzykiwanie zależności czyli Dependency Injection w 9 minut i 59 sekund. Część 3: jak źle używać DI</title>
		<link>http://www.xoft.pl/2009/04/10/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-3-jak-zle-uzywac-di/</link>
		<comments>http://www.xoft.pl/2009/04/10/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-3-jak-zle-uzywac-di/#comments</comments>
		<pubDate>Fri, 10 Apr 2009 08:22:57 +0000</pubDate>
		<dc:creator>Piotr Kochański</dc:creator>
				<category><![CDATA[DEPENDENCY INJECTION]]></category>
		<category><![CDATA[JAVA]]></category>
		<category><![CDATA[WSTRZYKIWANIE ZALEŻNOŚCI]]></category>

		<guid isPermaLink="false">http://www.xoft.pl/?p=92</guid>
		<description><![CDATA[Kontynuujemy dalej naszą przeprawę z DI [część 1., część 2.]. W tym odcinku postaramy się w końcu coś popsuć &#8211; radosny marsz ścieżką usłaną różami jest przyjemny ale nie zawsze pouczający.
Zastanówmy się na ile użycie wstrzykiwania zależności czyni nasze oprogramowanie odpornym na błędy przy projektowaniu architektury aplikacji. W końcu nawet najwspanialszy wzorzec projektowy nie zabroni [...]]]></description>
			<content:encoded><![CDATA[<p>Kontynuujemy dalej naszą przeprawę z DI [<a href="http://www.xoft.pl/2008/12/23/depedency-injection-spring-intro-baddesing">część 1.</a>, <a href="http://www.xoft.pl/2008/12/28/depedency-injection-spring-intro">część 2.</a>]. W tym odcinku postaramy się w końcu coś popsuć &#8211; radosny marsz ścieżką usłaną różami jest przyjemny ale nie zawsze pouczający.</p>
<p>Zastanówmy się na ile użycie wstrzykiwania zależności czyni nasze oprogramowanie odpornym na błędy przy projektowaniu architektury aplikacji. W końcu nawet najwspanialszy wzorzec projektowy nie zabroni nam popełniania błędów.</p>
<p>Przyjrzyjmy się nieco zmodyfikowanej klasie <code>NewsService</code></p>
<pre name="code" class="java">
public class NewsService {

  Storage storage;
  Authenticator authenticator;

  public NewsService(Driver driver) {
    storage = new DBStorage(driver);
  }

  public Authenticator getAuthenticator() {
    return authenticator;
  }

  public void setAuthenticator(Authenticator authenticator) {
    this.authenticator = authenticator;
  }

  public void login(String uname, String pass){
    ((UsernamePassAuthenticator)authenticator).setUname(uname);
    ((UsernamePassAuthenticator)authenticator).setPass(pass);
  }

  public void addNews(String news){
    if(authenticator != null){
      authenticator.authenticate();
    }
    storage.save(news.getBytes());
    System.out.println("News saved...");
  }
}
</pre>
<p>Różni się ona od poprzedniej wersji w jednym drobnym, ale bardzo istotnym szczególe. Klasa <code>NewsService</code> w konstruktorze pobiera jako parametr sterownik (obiekt <code>Driver</code>), a nie, jak poprzednio <code>Storage</code>. W rezultacie NewsService sam sobie musi utworzyć obiekt <code>Storage</code>.</p>
<p>Kod <code>NewsService</code> jest wówczas związany na sztywno z konkretną implementacją kontenera na dane <code>DBStorage</code>, co jest oczywiście niepożądane. Co się zatem stało, co zrobiliśmy nie tak &#8211; w końcu używamy DI? A no popełniliśmy błąd polegający na tym, że wstrzykujemy do <code>NewsService</code> nie ten obiekt, co trzeba!</p>
<p><code>NewsService</code> nie potrzebuje do niczego informacji o użytym sterowniku, <code>NewsService</code> potrzebuje tylko konkretnej implementacji <code>Storage</code> i to ona już ma się martwić o sterownik.</p>
<p>Ok. tutaj przypadek był prosty i ewidentny. W większych aplikacjach łatwo popełnić błąd polegający na wstrzyknięciu nie tego obiektu, który tak na prawdę jest wymagany. Także jeżeli widzimy, że gdzieś inicjalizacja klasy wymaga tworzenia obiektów, albo przekazujemy klasie jako parametr obiekty, które, logicznie rzecz biorąc, nie są jej potrzebne, to znaczy, że gdzieś położyliśmy architekturę aplikacji i błędnie użyliśmy DI.</p>
<p>Użycie DI wcale nie uwalnia nas od dobrego zaprojektowania aplikacji!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xoft.pl/2009/04/10/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-3-jak-zle-uzywac-di/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Wstrzykiwanie zależności czyli Dependency Injection w 9 minut i 59 sekund. Część 2: o tym, co tak właściwie robi DI</title>
		<link>http://www.xoft.pl/2008/12/28/depedency-injection-spring-intro/</link>
		<comments>http://www.xoft.pl/2008/12/28/depedency-injection-spring-intro/#comments</comments>
		<pubDate>Sun, 28 Dec 2008 16:09:58 +0000</pubDate>
		<dc:creator>Piotr Kochański</dc:creator>
				<category><![CDATA[DEPENDENCY INJECTION]]></category>
		<category><![CDATA[JAVA]]></category>

		<guid isPermaLink="false">http://www.xoft.pl/?p=59</guid>
		<description><![CDATA[W poprzednim wpisie znęcaliśmy się nad niezbyt gramotnie napisanym kawałkiem oprogramowania. Teraz przyszedł czas na napisanie wszystkiego tak, jak trzeba, w czym nam pomagać będzie właśnie wstrzykiwanie zależności.
Co nas najbardziej uwierało w poprzedniej wersji aplikacji? Tak na prawdę były to dwie rzeczy:

Powiązania między klasami były zrealizowane przy użyciu referencji do konkretnych klas: jeżeli NewsService potrzebował [...]]]></description>
			<content:encoded><![CDATA[<p>W <a href="http://www.xoft.pl/2008/12/23/depedency-injection-spring-intro-baddesing">poprzednim wpisie</a> znęcaliśmy się nad niezbyt gramotnie napisanym kawałkiem oprogramowania. Teraz przyszedł czas na napisanie wszystkiego tak, jak trzeba, w czym nam pomagać będzie właśnie wstrzykiwanie zależności.</p>
<p>Co nas najbardziej uwierało w poprzedniej wersji aplikacji? Tak na prawdę były to dwie rzeczy:</p>
<ol>
<li>Powiązania między klasami były zrealizowane przy użyciu referencji do konkretnych klas: jeżeli <code>NewsService</code> potrzebował mechanizmu do przechowywania informacji, to dawaliśmy mu referencję do klasy, która potrafiła przechowywać dane w SQL-owej bazie danych. Zmiana sposobu przechowywania danych na inny wymagała zmian w wielu miejscach w kodzie.</li>
<li>Jeżeli klasa potrzebowała do pracy innej klasy, to sama musiała sobie utworzyć odpowiedni obiekt: <code>NewsService</code> potrzebując klasy do przechowywania danych sam sobie tworzył jej instancję.</li>
</ol>
<p>Co w takim razie robi wstrzykiwanie zależności? Mówiąc ogólnie, w DI chodzi o to, żeby nie wiązać się z inną klasą poprzez użycie jej implementacji, tylko poprzez interfejs, pod który można podpiąć dowolną klasę go implementującą. </p>
<p>Dodatkowo, jeżeli nasza klasa potrzebuje konkretnej implementacji tego interfejsu, to nie powinna tej implementacji sama szukać czy jej tworzyć. Odpowiedni obiekt musi zostać do klasy &#8220;wstrzyknięty&#8221; w momencie inicjalizacji.</p>
<p>Zobaczmy, jak to wygląda w praktyce.<br />
<span id="more-59"></span><br />
Zacznijmy od klasy <code>NewsService</code>, której bezpośrednio używają klasy &#8220;interfejsu użytkownika&#8221;:<br />
<a name="listing.NewsService"></a>
<pre name="code" class="java">
public class NewsService {

  Storage storage;
  Authenticator authenticator;

  public NewsService() {
  }

  public NewsService(Storage storage) {
    this.storage = storage;
  }

  public Authenticator getAuthenticator() {
    return authenticator;
  }

  public void setAuthenticator(Authenticator authenticator) {
    this.authenticator = authenticator;
  }

  public Storage getStorage() {
    return storage;
  }

  public void setStorage(Storage storage) {
    this.storage = storage;
  }

  public void login(String uname, String pass){
    ((UsernamePassAuthenticator)authenticator).setUname(uname);
    ((UsernamePassAuthenticator)authenticator).setPass(pass);
  }

  public void addNews(String news){
    if(authenticator != null){
      authenticator.authenticate();
    }
    storage.save(news.getBytes());
    System.out.println("News saved...");
  }
}
</pre>
<p>Pierwsza rzecz, którą zrobiliśmy, to usunęliśmy referencje do konkretnej implementacji klasy odpowiedzialnej za przechowywanie danych (<code>DBStorage</code>) i zamiast niej umieściliśmy referencję do <em>interfejsu</em> <code>Storage</code>:</p>
<pre name="code" class="java">
public interface Storage {
  void save(byte[] data);
}
</pre>
<p>Dzięki temu będziemy mogli w klasie <code>NewsService</code> używać dowolnej implementacji <code>Storage</code>. Inicjalizując klasę <code>NewsService</code> będziemy jej &#8220;wstrzykiwać&#8221; odpowiednią klasę implementującą interfejs <code>Storage</code>. Z praktycznego punktu widzenia na tym właśnie polega cała magia Dependency Injection!</p>
<p>Szczególnie warto zwrócić uwagę na kluczowy element DI. W klasie <code>NewsService</code> nie ma kodu, który by wyszukiwał implementację interfejsu <code>Storage</code> &#8211; gdybyśmy coś takiego umieścili, to zamiast DI używalibyśmy wzorca projektowego <em>service locator</em>, który jest obecnie nieco <em>passe</em>. <code>NewsService</code> oczekuje, że w czasie inicjalizacji ktoś mu poda odpowiednią implementację <code>Storage</code>. W naszym przypadku robi to klasa <code>Client</code>, której listing jest na samym dole wpisu.</p>
<p>Komunikację między klasami poprzez interfejsy demonstruje w sposób wyraźny poniższy diagram klas dla aplikacji:</p>
<p><a href="http://www.xoft.pl/wp-content/uploads/2008/12/news-service-di-uml.png"><img src="http://www.xoft.pl/wp-content/uploads/2008/12/news-service-di-uml.png" alt="" title="news-service-di-uml" width="489" height="576" class="alignnone size-full wp-image-76" /></a></p>
<p>Widać, że tak, jak chcieliśmy, <code>NewsService</code> zależy od innych klas wyłącznie poprzez interfejsy, nie przez implementacje.</p>
<p>Analogicznie <code>DBStorage</code> zależy od sterownika także poprzez interfejs <code>Driver</code>. W poprzedniej wersji aplikacji <code>DBStorage</code> był powiązany z konkretną wersją sterownika &#8211; <code>DBDriver</code>. Zobaczmy, jak wygląda kod, który implementuje zawartość diagramu.</p>
<pre name="code" class="java">
public class DBStorage implements Storage{

  Driver driver;

  public DBStorage(Driver driver) {
    this.driver = driver;
  }

  public void save(byte[] data) {
    driver.openConnection();
    System.out.println("Saving in database: '" + new String(data) +"'");
    driver.closeConnection();
  }
}
</pre>
<p>Sam interfejs <code>Driver</code> wygląda następująco:</p>
<pre name="code" class="java">
public interface Driver {
  void openConnection();
  void closeConnection();
}
</pre>
<p>Konkretną implementacją sterownika jest u nas klasa <code>SqlDbDriver</code>:</p>
<pre name="code" class="java">
public class SqlDbDriver implements Driver{

  public void openConnection() {
    System.out.println("Openning the database connection");
  }

  public void closeConnection() {
    System.out.println("Closing the database connection");
  }
}
</pre>
<p>Pozostała jeszcze z poprzedniej wersji aplikacji byle jak zaimplementowana polityka uwierzytelniania. Teraz zrobimy to lepiej. W klasie <code>NewsService</code>, zgodnie z duchem DI, umieściliśmy odwołanie do interfejsu <code>Authenticator</code>:</p>
<pre name="code" class="java">
public interface Authenticator {
  void authenticate() throws AuthException;
}
</pre>
<p><code>AuthException</code> jest wyjątkiem czasu wykonywania:</p>
<pre name="code" class="java">
public class AuthException extends RuntimeException{

  public AuthException(Throwable cause) {
    super(cause);
  }

  public AuthException(String message, Throwable cause) {
    super(message, cause);
  }

  public AuthException(String message) {
    super(message);
  }

  public AuthException() {
  }
}
</pre>
<p>W naszym kodzie używamy konkretnej implementacji uwierzytelniania:</p>
<pre name="code" class="java">
public class UsernamePassAuthenticator implements Authenticator{
  private String uname;
  private String pass;

  public UsernamePassAuthenticator() {
  }

  public String getPass() {
    return pass;
  }

  public void setPass(String pass) {
    this.pass = pass;
  }

  public String getUname() {
    return uname;
  }

  public void setUname(String uname) {
    this.uname = uname;
  }

  public UsernamePassAuthenticator(String uname, String pass) {
    this.uname = uname;
    this.pass = pass;
  }

  /**
   * Username &#038; password authenticator: username must be the same as
   * password - do not use it in production system ;)
   */
  public void authenticate() {
    if(!uname.equals(pass)){
      throw new AuthException("Authorization failed");
    }
  }
}
</pre>
<p>No i wreszcie nagroda za nasze wysiłki, czyli klasa kliencka:</p>
<pre name="code" class="java">
public class Client {
  public static void main(String[] args) {
    Authenticator authenticator = new UsernamePassAuthenticator("beer","beer");
    Driver driver = new SqlDbDriver();

    //inject dependency #1
    Storage storage = new DBStorage(driver);
    //inject dependency #2
    NewsService newsService = new NewsService(storage);
    //inject dependency #3
    newsService.setAuthenticator(authenticator);

    newsService.addNews("ble ble ble");
  }
}
</pre>
<p>Widzimy, że przed użyciem <code>NewsService</code> musimy go skonfigurować, czyli &#8220;wstrzyknąć&#8221; mu obiekty klas, które są potrzebne mu do działania.</p>
<p>Na koniec ćwiczenie dla szanownego czytelnika. W końcu czasem warto się sprawdzić. Czy teraz już nasza aplikacja na prawdę wygląda tak, jak trzeba? Na pewno? Czy może gdzieś jeszcze coś nie gra tak jak trzeba? Może gdzieś oszukałem? Jakiś mały problemik, architektoniczna niedoskonałość?</p>
<p>???</p>
<p>Jeżeli ktoś poczuł ducha DI, to pewnie od razu zorientował się, gdzie jeszcze tkwi problem. W klasie <code>NewsService</code> nadal tkwi ukryta zależność od klasy konkretnej zamiast interfejsu &#8211; w metodzie <code>login</code> klasy <code>NewsService</code> jest robione rzutowanie na klasę konkretną <code>UsernamePassAuthenticator</code>.</p>
<p>Zależność ta jest o tyle nieprzyjemna, że można ją na pierwszy rzut oka przeoczyć, w szczególności nie widać jej na diagramie UML-owym.</p>
<p>Warto się pozbyć się tego niezbyt eleganckiego kodu, świadczy on tylko o tym, że źle zaplanowaliśmy odpowiedzialność klasy <code>NewsService</code>. Niby dlaczego nagle ma ona zajmować się logowaniem, skoro może to zrobić <code>Authenticator</code>. Metoda <code>login</code> jest zwyczajnie zbędna.</p>
<p>To prowadzi nas z kolei do następnego pytania, na ile łatwo jest błędnie zaprojektować architekturę aplikacji, gdy zdecydujemy się używać wstrzykiwania zależności? Zajmiemy się tym problemem w <a href="http://www.xoft.pl/2009/04/10/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-3-jak-zle-uzywac-di">trzeciej</a> części cyklu.</p>
<p>Wreszcie dociekliwy czytelnik może zacząć się zastanawiać, jak będzie wyglądało to wstrzykiwanie zależności, jeżeli będziemy często używać <code>NewsService</code> &#8211; w końcu za każdym razem będziemy musieli mu wstrzykiwać te potrzebne klasy, co oznacza pisanie w kółko tego samego kodu. Czy nie lepiej jest użyć jednak jakiego wzorca <em>factory</em> czy <em>service locator</em>, żeby jednak <code>NewsService</code> sam sobie znalazł potrzebne klasy. Z tym problemem można sobie także poradzić najłatwiej przy użyciu kontenera <em>Dependency Injection</em>, takiego jak Spring, PicoContainer czy Guice. Temu będzie poświęcony <a href="http://www.xoft.pl/2009/04/30/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-4-wykorzystanie-spring-a">czwarty</a> wpis z serii DI.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xoft.pl/2008/12/28/depedency-injection-spring-intro/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Wstrzykiwanie zależności, czyli Dependency Injection w 9 minut i 59 sekund. Część 1: podręczny przewodnik tworzenia złych aplikacji</title>
		<link>http://www.xoft.pl/2008/12/23/depedency-injection-spring-intro-baddesing/</link>
		<comments>http://www.xoft.pl/2008/12/23/depedency-injection-spring-intro-baddesing/#comments</comments>
		<pubDate>Tue, 23 Dec 2008 12:02:51 +0000</pubDate>
		<dc:creator>Piotr Kochański</dc:creator>
				<category><![CDATA[DEPENDENCY INJECTION]]></category>
		<category><![CDATA[JAVA]]></category>
		<category><![CDATA[SPRING]]></category>

		<guid isPermaLink="false">http://www.xoft.pl/?p=40</guid>
		<description><![CDATA[Gdzie się człowiek nie obejrzy tam się czai Dependency Injection (DI), czyli wstrzykiwanie zależności. Żeby jeszcze było tego mało, jak się zaczyna grzebać w internecie, to się co chwila można potknąć o jakiś framework, kontener czy coś takiego, co nam samo może zrobić Dependency Injection.
Co to w ogóle jest, po co takie coś komukolwiek, jak [...]]]></description>
			<content:encoded><![CDATA[<p>Gdzie się człowiek nie obejrzy tam się czai Dependency Injection (DI), czyli wstrzykiwanie zależności. Żeby jeszcze było tego mało, jak się zaczyna grzebać w internecie, to się co chwila można <a href="http://en.wikipedia.org/wiki/Dependency_injection#Existing_frameworks">potknąć</a> o jakiś framework, kontener czy coś takiego, co nam samo może zrobić Dependency Injection.</p>
<p>Co to w ogóle jest, po co takie coś komukolwiek, jak tego używać? Okazuje się, że sprawa jest prosta, wręcz banalna, a jednocześnie prowadzi do całkiem interesujących zastosowań. Za chwilę postaramy się zrozumieć jak DI działa, zrobimy to w sposób praktyczny, obdarty z krążących wokół DI ideologii i zupełnie zbędnego adżajlowego bełkotu.</p>
<p>Przekonamy się, że DI jest jednym z wielu możliwych sposobów budowania architektury aplikacji tak, by była łatwa w rozbudowie i testowaniu.</p>
<p>Będziemy chcieli zobaczyć coś więcej, niż zupełnie trywialny przykład (takich jest mnóstwo w internecie), po którym w zasadzie można wzruszyć ramionami, bo nie specjalnie widać tam jakiekolwiek zalety architektury wykorzystującej DI. </p>
<p>Naszym celem będzie zbudowanie bardzo prostej aplikacji służącej do przechowywania informacji, będzie składała się ona z trzech warstw (trójka jest nieprzypadkowa, sporo aplikacji Java EE jest rozbijane na tyle warstw) i tyluż komponentów.</p>
<p>Naszą aplikację napiszemy na kilka sposobów:</p>
<ul>
<li>naiwne podeście z paskudną architekturą;</li>
<li><a href="http://www.xoft.pl/2008/12/28/depedency-injection-spring-intro">w miarę rozsądnie wyglądająca aplikacja</a>, wykorzystująca wstrzykiwanie zależności;</li>
<li><a href="http://www.xoft.pl/2009/04/10/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-3-jak-zle-uzywac-di">aplikacja wykorzystująca wstrzykiwanie zależności, ale zrobiona źle</a> &#8211; warto wiedzieć, że wbrew wrażeniu, jakie można odnieść po ohah i ahah na temat DI, przy jego pomocy też można koncertowo skopać architekturę tworzonego oprogramowania.</li>
<li><a href="http://www.xoft.pl/2009/04/30/wstrzykiwanie-zaleznosci-czyli-dependency-injection-w-9-minut-i-59-sekund-czesc-4-wykorzystanie-spring-a">ta sama aplikacja, wykorzystująca wstrzykiwanie zależności i kontener Spring;</a></li>
<li><a href="/2009/07/09/wstrzykiwanie-zaleznosci-czyli-dependency-injection-czesc-5-wykorzystanie-google-guice/">jeszcze raz to samo, ale z wykorzystaniem Google Guice</a>;</li>
<li>następnie spróbujemy porównać działanie Spring i Google Guice;</li>
<li>wreszcie, na końcu trochę &#8220;filozofii&#8221;, czy faktycznie DI jest potrzebne, czy jest potrzebne tylko Javie, jak to jest z innymi językami.</li>
</ul>
<p>Zatem, do dzieła!<br />
<span id="more-40"></span><br />
Zaczniemy od pierwszej wersji naszej aplikacji. Głównym jej elementem jest klasa <code>NewsService</code>, która używa do przechowywania informacji klasy <code>DBStorage</code>, która to z kolei potrzebuje sterownika komunikującego się z bazą danych (<code>DBDriver</code>). Chcemy mieć także możliwość zmuszenia użytkownika naszej aplikacji do uwierzytelnienia się &#8211; zajmuje się tym <code>DBStorage</code> i <code>NewsService</code>. </p>
<p>Zobaczmy sobie po kolei te klasy:</p>
<pre name="code" class="java">
public class NewsService {
  DBStorage dBStorage = new DBStorage();

  public void addNews(String news){
    addNews(news, new DBStorage());
  }

  public void addNews(String news, String uname, String pass){
    addNews(news, new DBStorage(uname, pass));
  }

  public void addNews(String news, DBStorage dBStorage){
    this.dBStorage = dBStorage;
    dBStorage.save(news.getBytes());
  }
}
</pre>
<p>W tej klasie ważne są dwie rzeczy: użycie <code>DBStorage </code> oraz oczywiście metoda <code>addNews</code>.</p>
<pre name="code" class="java">
public class DBStorage {

  DBDriver driver;

  public DBStorage(String uname, String pass) {
    authenticate(uname, pass);
    driver = DBDriver.getInstance();
  }

  public DBStorage() {
    driver = DBDriver.getInstance();
  }

  public void authenticate(String uname, String pass){
    System.out.println("Authentication...");
  }

  public void save(byte[] data){
    driver.openConnection();
    System.out.println("Saving in database...");
    driver.closeConnection();
  }
}
</pre>
<p>Klasa <code>DBStorage</code> dzielnie sama sobie produkuje <code>DBDriver</code> i jeszcze na dodatek zajmuje się uwierzytelnianiem użytkowników. Metoda <code>save</code> wykonuje czarną robotę zapisywania danych w odpowiednim miejscu.</p>
<pre name="code" class="java">
public class DBDriver {

  private DBDriver() {
  }

  public static DBDriver getInstance(){
    return new DBDriver();
  }

  public void openConnection(){
    System.out.println("Openning the connection");
  }

  public void closeConnection(){
    System.out.println("Closing the connection");
  }
}
</pre>
<p>Zobaczmy też na listingu poniżej jak tego wszystkiego można użyć razem:</p>
<pre name="code" class="java">
public class Client {
  public static void main(String[] args) {
    NewsService newsService = new NewsService();
    newsService.addNews("ble ble ble");

    DBStorage dbStorage1 = new DBStorage();
    newsService.addNews("bla bla bla", dbStorage1);

    DBStorage dbStorage2 = new DBStorage("joe","beer");
    newsService.addNews("bla bla bla", dbStorage2);
  }
}
</pre>
<p>No dobrze, kod się kompiluje, działa, wydawałoby się, że wszystko jest ok, ale&#8230; No właśnie, coś tutaj jednak nie gra.</p>
<p>Pierwsza rzecz. Wyobraźmy sobie, że chcemy teraz zapisywać dane nie w bazie danych tylko, powiedzmy, w pliku XML-owym, albo pliku JSON czy CSV. Albo może nasze newsy mają być przechowywane jako zserializowane obiekty Java. Jednakże klasa <code>NewsService</code> zależy od klasy <code>DBStorage</code>, która potrafi dane przechowywać tylko w bazie danych.</p>
<p>Mało tego, klasa <code>DBStorage</code> jest powiązana ściśle z <code>DBDriver</code>, także podmiana klasy sterownika również nie jest prosta.</p>
<p>Jedyne wyście jakie nam pozostaje, gdy chcemy zmienić sposób działania aplikacji, to dodawać do klasy <code>NewsService</code> kolejne metody dodające informację, które będą wykorzystywały inne sposoby przechowywania danych. Powstanie w ten sposób kod, który będzie się w dużej mierze powtarzał, klasa <code>NewsService</code> będzie się coraz bardziej rozrastała, a jej użycie coraz trudniejsze.</p>
<p>Świetnie, krytykować zawsze jest łatwo. A co, jeśli docelowym sposobem przechowywania informacji jest baza danych i nie będziemy tutaj nic zmieniać?</p>
<p>To nas prowadzi do drugiego problemu &#8211; testowalności tej aplikacji. Jedynym sposobem testowania klasy <code>NewsService</code> jest dostarczenie jej instancji klasy <code>DBStorage</code> &#8211; bez niej <code>NewsService</code> nie może w ogóle działać. <code>DBStorage</code> z kolei używa konkretnego sterownika do bazy danych.</p>
<p>W rezultacie, jeśli chcemy napisać test jednostkowy, czy test funkcjonalny musimy odtworzyć pełne środowisko działania aplikacji. Jeżeli nawet da się to zrobić (chociaż może wcale tak nie być i często w rzeczywistych sytuacjach nie jest), to testy będą znacznie trudniejsze do napisania, będą dłużej się wykonywały. Przy takiej aplikacji jak nasza, pewnie to nie jest problemem, ale pracując z 500 tysiącami linii kodu może być to istotna sprawa.</p>
<p>Na koniec jeszcze jeden rzecz &#8211; problem uwierzytelniania. Abstrahując od tego, że przywiązujemy się tylko do jednego sposobu uwierzytelniania (hasło + nazwa użytkownika), to jeszcze sam proces uwierzytelniania jest rozwleczony pomiędzy klasę <code>NewsService</code> i <code>DBStorage</code>.</p>
<p>Co jest przyczyną wymienionych problemów? Popatrzmy sobie na to, co zrobiliśmy z lotu ptaka:</p>
<a href="http://www.xoft.pl/wp-content/uploads/2008/12/news-service-bad-design-uml.png"><img src="http://www.xoft.pl/wp-content/uploads/2008/12/news-service-bad-design-uml.png" alt="News Service - bad design" title="news-service-bad-design-uml" width="417" height="320" class="size-full wp-image-52" /></a>
<p>Teraz widać jaśniej, co zrobiliśmy źle:</p>
<ul>
<li>Klasa <code>NewsService</code> zależy od konkretnej implementacji <code>DBStorage</code>, przez co nie możemy go łatwo wymienić na inny typ kontenera na dane. </li>
<li><code>DBStorage</code> zależy także bezpośrednio od <code>DBDriver</code> &#8211; tutaj sytuacja jest odrobinę lepsza, bo <code>DBDriver</code> jest tworzony przy pomocy metody <em>factory</em>, co daję pewną elastyczność tworzenia tego obiektu. Niestety z punktu widzenia testowalności niewiele to nam daje &#8211; chcielibyśmy w miejsce tego sterownika podstawić obiekt symulujący zachowanie sterownika, na co obecna implementacja nie pozwala</li>
<li>W przypadku uwierzytelniania źle zaplanowaliśmy odpowiedzialność klas i zaszyliśmy na stałe konkretny sposób uwierzytelniania</li>
</ul>
<p>Jak przemodelować tę aplikację, żeby pozbyć wyeliminować powyżej opisane jej wady? Tym zajmiemy się w <a href="http://www.xoft.pl/2008/12/28/depedency-injection-spring-intro">kolejnym wpisie</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xoft.pl/2008/12/23/depedency-injection-spring-intro-baddesing/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Aplikacja w Swing Application Framework (prosta, ale nie za prosta)</title>
		<link>http://www.xoft.pl/2008/05/13/aplikacja-w-swing-application-framework-prosta-ale-nie-za-prosta/</link>
		<comments>http://www.xoft.pl/2008/05/13/aplikacja-w-swing-application-framework-prosta-ale-nie-za-prosta/#comments</comments>
		<pubDate>Tue, 13 May 2008 10:53:43 +0000</pubDate>
		<dc:creator>Piotr Kochański</dc:creator>
				<category><![CDATA[JAVA]]></category>
		<category><![CDATA[SWING]]></category>
		<category><![CDATA[GUI]]></category>
		<category><![CDATA[SAF]]></category>
		<category><![CDATA[Swing Application Framework]]></category>

		<guid isPermaLink="false">http://www.xoft.pl/2008/05/13/aplikacja-w-swing-application-framework-prosta-ale-nie-za-prosta/</guid>
		<description><![CDATA[Naszym celem jest utworzenie prostej aplikacji szukającej plików na dysku, gotową aplikację można uruchomić przez Java WebStart, dostępny jest oczywiście kod źródłowy, powiedzmy, że na licencji BSD ;).
Wstęp
Swing Application Framework jest szkieletem aplikacyjnym, który ma uprościć tworzenie aplikacji desktopowych w Java Swing. SAF z założenia ma być rozwiązaniem prostym, rozwiązującym podstawowe problemy, na jakie napotykamy [...]]]></description>
			<content:encoded><![CDATA[<p>Naszym celem jest utworzenie prostej aplikacji szukającej plików na dysku, gotową aplikację można <a href="http://www.xoft.pl/wp-content/uploads/2008/03/szukacz.jnlp">uruchomić</a> przez Java WebStart, dostępny jest oczywiście <a href="http://www.xoft.pl/wp-content/uploads/2008/03/szukacz.zip">kod źródłowy</a>, powiedzmy, że na licencji BSD ;).</p>
<p><strong>Wstęp</strong><br />
<a href="https://appframework.dev.java.net/" title="Strona domowa SAF" target="_blank">Swing Application Framework</a> jest szkieletem aplikacyjnym, który ma uprościć tworzenie aplikacji desktopowych w Java Swing. SAF z założenia ma być rozwiązaniem prostym, rozwiązującym podstawowe problemy, na jakie napotykamy się tworząc programy z interfejsem użytkownika w Javie.</p>
<p>Czym więc zajmuje się SAF?</p>
<ul>
<li>Cyklem życia aplikacji od jej uruchomienia po zamknięcie</li>
<li>Zarządzaniem zasobami: łańcuchami znaków, kolorami, ikonami, czcionkami itp. rzeczami, które występują w typowej aplikacji. Oczywiście wszystkie zasoby mogą być internacjonalizowane/lokalizowane.</li>
<li>Obsługą zdarzeń (co ma się stać, gdy użytkownik naciśnie przycisk X). W szczególności SAF upraszcza znacząco obsługę długotrwałych zdarzeń, które powinny wykonywać się w wątkach roboczych, a nie głównym</li>
<li>Pamiętaniem stanu aplikacji. Po zamknięciu aplikacji pamięta ona jaki był stan interfejsu użytkownika (rozmiar okien, ich położenie) przed zamknięciem.</li>
</ul>
<p>Żeby nie przedłużać wstępu przejdźmy do rzeczy, czyli przyjrzyjmy się przykładowej aplikacji <a href="http://www.xoft.pl/wp-content/uploads/2008/03/szukacz.zip">Szukacz</a>. <em>Szukacz</em> jest kulawą i ubogą namiastką <a href="http://pl.wikipedia.org/wiki/Grep" target="_blank">grep</a>-a napisaną w Javie, ma za to graficzny interfejs użytkownika.</p>
<p>Aplikacja jest skonstruowana inaczej niż w większości tutoriali Swing Application Framework, które można znaleźć w Internecie. Problem z umieszczonymi tam przykładami jest taki, że w rzeczywistej sytuacji są one mało użyteczne. Przykłady te dzielą się na dwie grupy:</p>
<ol>
<li>aplikacje wyklikane od początku do końca w środowisku NetBeans, korzystające z kreatorów kodu tam dostępnych</li>
<li>aplikacje napisane całkowicie ręcznie, włącznie z elementami GUI</li>
</ol>
<p>Tak na prawdę żadna z tych sytuacji nie jest typowa.</p>
<p>Ad. 1. Nie każdy musi chcieć używać NetBeansa (fakt, jest bardzo dobry jeśli chodzi o tworzenie GUI), a nawet jeżeli używamy go, to istnieje duża szansa, że mamy już napisany jakiś spory kawał kodu, który nie używa SAF i chcielibyśmy jakoś gładko istniejący kod zintegrować z tym frameworkiem.</p>
<p>Ad. 2. Jeśli nie jesteśmy maniakalnymi zwolennikami ręcznego dziergania kodu GUI, co zazwyczaj kończy się mniejszą lub większą katastrofą połączoną z gromami rzucanymi na Swinga i Javę w ogóle, to pewnie chcemy użyć jakiegoś wizulanego narzędzi, które pozwoli nam wyklikać strukturę GUI. Narzędzie to na bank nie słyszało jeszcze o SAF, a my chcemy sobie klikać interfejs użytkownika i jednocześnie łatwo go zintegrować z SAF.</p>
<p>Tak więc chcę pokazać, jak tworzyć sobie GUI takim sposobem, jak nam się podoba i móc go w każdej chwili zintegrować z SAF. Zatem do dzieła.<span id="more-26"></span></p>
<p><strong>Logika &#8220;biznesowa&#8221; aplikacji</strong><br />
Zacznijmy od bardzo szybkiego spojrzenia na klasę <a href="http://www.xoft.pl/wp-content/uploads/2008/03/grep.html" title="pl.xoft.saf.finder.Grep.java">Grep.java</a>, która zajmuje się wyszukiwaniem podanego wyrażenie regularnego w plikach. Inicjalizujemy ją podając wyrażenie regularne, które kompilujemy ze względów wydajnościowych do obiektu <a href="http://java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html" target="_blank">java.util.regex.Pattern</a>. Całą pracę wykonuje metoda <code>void searchInFiles(File)</code>, która złośliwie nie zwraca wyników wyszukiwania. Możemy je otrzymać w formie miłej ludzkiemu oku metodą <code>String getFormatedSearchResults()</code>, a w formie miłej programistą dzięki metodzie <code>Map&lt;String, Map&lt;Integer, String&gt;&gt; getSearchResults()</code>. Zresztą kod mówi sam za siebie.</p>
<p><strong>SAF &#8211; cykl życia aplikacji</strong><br />
Zajmiemy się teraz podstawowym kawałkiem SAF, czyli główną klasą aplikacji. Musi ona dziedziczyć po klasie <code>Application</code> lub <code>SingleFrameApplication</code> frameworka. Klasa <code>SingleFrameApplication</code> daje nam kilka dodatkowych usług oraz gotową instancję klasy reprezentującej okno w Swingu, czyli <a href="http://java.sun.com/javase/6/docs/api/javax/swing/JFrame.html" target="_blank">JFrame</a>.  Z powodów opisanych powyżej będziemy jednak samodzielnie tworzyć okno aplikacji &#8211; chcemy używać SAF, ale nie chcemy, żeby nam się on w aplikacji za bardzo panoszył. Klasa <code>Application</code> jest abstrakcyjna, dziedzicząc po niej musimy zaimplementować samodzielnie metodę <code>void startup()</code>, za chwilę się nią zajmiemy.</p>
<p>Popatrzmy na listing klasy <a href='http://www.xoft.pl/wp-content/uploads/2008/03/mainapp.html' title='MainApp.java'>MainApp.java</a>.</p>
<pre name="code" class="java">
package pl.xoft.saf.finder.ui;

import java.awt.Component;
import java.util.EventObject;
import javax.swing.JOptionPane;
import org.jdesktop.application.*;

public class MainApp extends SingleFrameApplication{
  ResourceMap resource;
  ApplicationContext ctxt;

  @Override
  protected void startup() {
    FrameView view = new MainViewFrame(this);
    view.setFrame(new MainFrame());
    show(view);

    addExitListener(new ExitListener() {
      public boolean canExit(EventObject e) {
        Object[] options = {resource.getString("label.yes"),
            resource.getString("label.no")};

        Object source = (e != null) ? e.getSource() : null;
        Component owner =
            (source instanceof Component) ?
                (Component)source : null;

        boolean mayExit = JOptionPane.showOptionDialog(
            owner,
            resource.getString("label.exit"),
            resource.getString("Application.name"),
            JOptionPane.YES_NO_OPTION,
            JOptionPane.QUESTION_MESSAGE,
            null,
            options,
            options[1]) == JOptionPane.YES_OPTION;

        return mayExit;
      }
      public void willExit(EventObject event) {
        //do nothing
      }
    });
  }

  @Override
  protected void initialize(String[] args) {
    System.out.println("Inicjalizacja... ");
    this.ctxt = getContext();
    ResourceManager mgr = ctxt.getResourceManager();
    resource = mgr.getResourceMap(MainApp.class);
  }

  @Override
  protected void shutdown() {
    System.out.println("Koniec pracy!! Czyścimy");
  }

  public static void main(String[] args) {
    Application.launch(MainApp.class, args);
  }
}
</pre>
<p>Żeby było inaczej niż zwykle, zacznijmy od końca, czyli metody <code>main(String[])</code>, przy pomocy metody <code>Application.launch(Application)</code> uruchamiamy całą aplikację. Metoda ta jest bardzo wygodna dla nas, bo nie musimy pamiętać o inicjalizacji GUI z poziomu odpowiedniego wątku, Event dispatching thread (EDT). Zróbmy w tym miejscu mały skok w bok, żeby wyjaśnić sobie terminologię no i żeby osoby mniej obyte z tematem wiedziały w czym rzecz.</p>
<p>Trzeba pamiętać, że typowa aplikacja Swingowa potrzebuje co najmniej trzech wątków:</p>
<ol>
<li><em>głównego</em>, który powinien tylko uruchomić aplikację,</li>
<li><em>Event dispatching thread</em>, w którym, <em>i tylko w którym</em> można tworzyć i modyfikować komponenty interfejsu użytkownika,</li>
<li><em>wątku lub wątków roboczych</em>, które wykonują wszystkie dłużej trwające zadania &#8211; nie chcemy ich wykonywać w EDT, żeby nie blokować interfejsu użytkownika.</li>
</ol>
<p>Pierwszym często popełnianym błędem jest tworzenie GUI z poziomu wątku głównego, a nie EDT, często to nie powoduje problemów, ale zdarza się, że pojawiają się przykre i trudne do wykrycia błędy.</p>
<p>Metoda <code>lanuch()</code> powoduje rozpoczęcie wywołań kolejnych metod odpowiedzialnych za obsługę cyklu życia aplikacji, możemy przesłonić tylko te, które chcemy za wyjątkiem <code>void startup()</code>.</p>
<ul>
<li>Metoda <code>void initialize(String[])</code>. Możemy w niej wykonać różnego rodzaju czynności inicjalizacyjne, które są potrzebne, w szczególności, jeśli trzeba je wykonać przed konstrukcją interfejsu użytkownika</li>
<li>Metodę <code>void startup()</code> musimy przesłonić obowiązkowo: w tej metodzie inicjalizujemy tworzenie GUI. W metodzie tej tworzymy instancję klasy <code>FrameView</code>, która jest mostem pomiędzy SAF, a oknem aplikacji <code>MainFrame</code>. Potencjalnie <code>MainFrame</code> nie musi nic wiedzieć o SAF, dzięki czemu mamy dużą swobodę pracy z nią &#8211; nie jesteśmy uzależnieni od SAF, w każdej chwili możemy używać tych jego elementów, które chcemy.</li>
<li>Metody <code>void ready()</code> nie ma na listingu, przesłaniamy ją wtedy, gdy potrzebne są jakieś czynności inicjalizacyjne po utworzeniu GUI &#8211; zazwyczaj chcemy jak najszybciej pokazać użytkownikowi interfejs aplikacji, a gdy on się jemu z podziwem przygląda możemy spokojnie dokończyć inicjalizację.</li>
<li>Metoda <code>void exit()</code> kończy działanie aplikacji. Odbywa się to w ten sposób, że jeśli z poziomu aplikacji wywołane jest zdarzenie żądające zakończenia pracy (np. klikniemy przycisk &#8220;z krzyżykiem&#8221; okienka), to przejmowane jest ono przez klasę <code>ExitListener</code>-a, która sprawdza, czy wolno zakończyć działanie aplikacji. W naszym przypadku w metodzie <code>startup()</code> dodajemy <code>ExitListener</code>-a, który wyświetla okno dialogowe z pytaniem, czy możemy kończyć pracę.</li>
<li>Metoda <code>void shutdown()</code>. Gdy zapadnie decyzja, że możemy kończyć ostatnią rzeczą jaka jest robiona jest wywołanie tej metody; możemy tam posprzątać po sobie lub zrobić cokolwiek, co ma być ostatnią rzeczą robioną przez nasz program.</li>
</ul>
<p>Widzimy więc, że SAF automatyzuje i standaryzuje typowe elementy, które aplikacja powinna mieć: prawidłową inicjalizację i zakończenie działania.</p>
<p><strong>Zarządzanie zasobami</strong><br />
Kolejnym elementem, który daje nam SAF jest zarządzanie zasobami. Dotyczy to wszystkich zasobów: napisów na etykietach, przyciskach, itp., ikon, używanych czcionek. W jaki sposób to się odbywa? Zasoby dzielą się na globalne i lokalne.</p>
<p>Zasoby globalne są umieszczone w pliku <code>GlownaKlasaAplikacji.properties</code>, który musi się znajdować w pakiecie <code>nazwa.pakietu.resources</code>, przy założeniu, że klasa <code>GlownaKlasaAplikacji</code> znajduje się w pakiecie <code>nazwa.pakietu</code>. W naszym przypadku zasoby globalne znajdziemy w pliku <code>MainApp.properties</code> i dla języka angielskiego w <code>MainApp_en_US.properties</code>. Oba pliki są w pakiecie <code>pl.xoft.saf.finder.ui.resources</code>.</p>
<p>Zasoby lokalne są przechowywane z tą samą konwencją co powyżej &#8211; każda klasa ma swój plik z zasobami umieszczony w pliku <em>properties</em> o nazwie takiej samej jak klasa.</p>
<p>Do zasobów dostajemy się tak jak to demonstruje listing klasy <code>MainApp</code>. W metodzie <code>initialize(String[])</code> tworzymy uchwyt do bardzo użytecznej klasy, <code>ApplicationContext</code> (linia 49.), reprezentującej środowisko działania aplikacji. Następnie z kontekstu aplikacji wyciągamy klasę zarządzającą zasobami: <code>ResourceManager</code>. W klasie <code>MainApp</code> zasobów używamy przy tworzeniu okna dialogowego, które pojawia się, gdy ktoś chce zamknąć aplikację (linie 30 i 31).</p>
<p>Bardzo użyteczną cechą SAF jest automatyczna konwersja niektórych zasobów na klasy Java. Obejrzymy sobie fragment klasy <a href='http://www.xoft.pl/wp-content/uploads/2008/03/mainframe.html' title='MainFrame.html'>MainFrame.html</a>, w którym do aplikacji dodajemy element menu pozwalający zwiększyć wielkość czcionki.</p>
<pre class="brush: java; first-line: 66;">
        jMenuItem3.setAction(actionMap.get("makeLarger"));
        jMenuItem3.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, InputEvent.ALT_MASK | InputEvent.CTRL_MASK));
        jMenuItem3.setIcon(resourceMap.getIcon("jMenuItem3.icon"));
        jMenuItem3.setText(resourceMap.getString("jMenuItem3.text"));
        jMenuItem3.setName("jMenuItem3");
        jMenu2.add(jMenuItem3);
</pre>
<p>Aby menu było czytelniejsze jest tam umieszczona odpowiednia ikona. Bez SAF sami byśmy musieli z łańcuch znaków wyprodukować obiekt klasy <a href="http://java.sun.com/javase/6/docs/api/javax/swing/ImageIcon.html">ImageIcon</a>, a tutaj wystarczy, że użyjemy metody <code>ResourceMap.getIcon(String)</code>. Podobnie konwertowane są z łańcuchów znaków definicje czcionek i kolory. Niby drobne udogodnienie, ale jeśli dołożymy do tego internacjonalizację i pomnożymy wszystko przez 500 wystąpień w dużej aplikacji, to okaże się, że jednak trochę rękę oszczędziliśmy.</p>
<p><strong>Akcje &#8211; obsługa zdarzeń</strong><br />
Wreszcie dochodzimy do najciekawszego elementy SAF, uproszczonego definiowania akcji. W Swingu jeśli chcemy, żeby jakiś komponent oprócz siedzenia w okienku mógł zrobić coś pożytecznego, musieliśmy dodać do niego nasłuchiwacza zdarzeń (<code>ActionListener</code> lub innego) a następnie tegoż nasłuchiwacza zaimplementować jako osobną klasę. Często robi się to wykorzystując klasę wewnętrzną.</p>
<p>SAF całą sprawę bardzo upraszcza, po prostu tworzymy metodę, oznaczamy ją metadaną <code>@Action</code> i dodajemy informację o tym, że pojawiła się obsługa zdarzenia do mapy akcji.</p>
<p>Popatrzmy na poniższy fragment kodu, wzięty z klasy <a href='http://www.xoft.pl/wp-content/uploads/2008/05/mainpane.html' title='MainPane.java'>MainPane.java</a>:</p>
<pre name="code" class="java">
//fragmenty klasy MainPane
public class MainPane extends javax.swing.JPanel {

  File f = null;

  public MainPane() {
    initComponents();
  }

  //w metodzie pozostawiona jest tylko inicjalizacja interesujących nas w tej chwili komponentów
  private void initComponents() {
    jTextArea1 = new javax.swing.JTextArea();
    jButton1 = new javax.swing.JButton();
    jTextField2 = new javax.swing.JTextField();

    jTextArea1.setColumns(20);
    jTextArea1.setEditable(false);
    jTextArea1.setFont(resourceMap.getFont("jTextArea1.font"));
    jTextArea1.setRows(5);
    jTextArea1.setName("jTextArea1");

    ActionMap actionMap = Application.getInstance(MainApp.class).getContext().getActionMap(MainPane.class, this);
    jButton1.setAction(actionMap.get("search"));
    jButton1.setText(resourceMap.getString("jButton1.text"));
    jButton1.setName("jButton1"); 

    jTextField1.setEditable(false);
    jTextField1.setText(resourceMap.getString("jTextField1.text"));
    jTextField1.setName("jTextField1");
  }

  @Action
  public void search() {
    jTextArea1.setText("");
    Grep g = new Grep(jTextField2.getText());
    try {
      if(f != null)
        g.searchInFiles(f);
    } catch (FileNotFoundException ex) {
      Logger.getLogger(MainPane.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IOException ex) {
      Logger.getLogger(MainPane.class.getName()).log(Level.SEVERE, null, ex);
    }
    getJTextArea1().setText(g.getFormatedSearchResults());
  }
}
</pre>
<p>Interesuje nas wyłącznie obsługa przycisku &#8220;Szukaj&#8221;, dlatego cały kod służący do innych celów został usunięty z powyższego listingu. W linii 32. zaczyna się metoda <code>search</code>, oznaczamy ją metadaną <code>@Action</code>, żeby zarejestrować ją jako akcję. Pozostaje jeszcze powiązać odpowiedni przycisk (<code>JButton1</code>) z tę akcją. Robimy to w linii 23., wykorzystując klasę <code>javax.swing.ActionMap</code>.</p>
<p>Warto zwrócić uwagę na to, że możemy się odwoływać do akcji zdefiniowanych w innych klasach, przekazując odpowiedni obiekt do metody <code>ApplicationContext.getActionMap(Class, Object)</code> &#8211; jest to bardzo wygodne, gdyż raz zdefiniowaną akcję można wykorzystywać w całej aplikacji. </p>
<p>Pewnie najwygodniejszy byłby tutaj mechanizm wstrzykiwania zależności (ang. <em>dependency injection</em>), ale SAF ma być prosty, więc przynajmniej w obecnej wersji robimy to wyszukując odpowiednią klasę i metodę z kontekstu aplikacji.</p>
<p>Opisana implementacja funkcjonalności wyszukiwania jest bardzo prosta w implementacji, ma jednakże jedną dosyć zasadniczą wadę: jeżeli wyszukiwanie trwa dłużej, to blokuje ono interfejs użytkownika, w szczególności nie ma możliwości przerwania wyszukiwania. Dlaczego tak się dzieje jest jasne: długotrwałe zadanie wykonujemy w wątku EDT odpowiedzialnym za rysowanie interfesju użytkownika.</p>
<p>Musimy zatem uruchomić wyszukiwanie w osobnym wątku. Brzmi to dość prosto, ale w praktyce jest uciążliwe, gdyż trzeba zsynchronizować działanie wątku szukającego z wątkiem EDT. Dotychczas najwygodniejszym rozwiązaniem było wykorzystanie klasy narzędziowej <code>SwingWorker</code> [<a href="http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html">javadoc</a>, <a href="http://java.sun.com/developer/technicalArticles/javase/swingworker/index.html">przykład wykorzystania</a>]. Podobne podejście stosuje SAF, tyle, że użycie analogicznego mechanizmu jest prostsze niż w przypadku <code>SwingWorker</code>-a.</p>
<p>Przyjrzymy się jeszcze raz klasie <a href='http://www.xoft.pl/wp-content/uploads/2008/05/mainpane.html' title='MainPane.java'>MainPane.java</a>, tyle, że innemu jej fragmentowi </p>
<pre name="code" class="java">
//fragmenty klasy MainPane
public class MainPane extends javax.swing.JPanel {

  File f = null;

  public MainPane() {
    initComponents();
  }
   //w metodzie pozostawiona jest tylko inicjalizacja interesujących nas w tej chwili komponentów
  private void initComponents() {
    ActionMap actionMap = Application.getInstance(MainApp.class).getContext().getActionMap(MainPane.class, this);

    jButton2.setAction(actionMap.get("searchNoBlocking"));
    jButton2.setText(resourceMap.getString("jButton2.text"));
    jButton2.setName("jButton2");

    jTextField2.setText(resourceMap.getString("jTextField2.text"));
    jTextField2.setName("jTextField2");

    jButton4.setAction(actionMap.get("cancel"));
    jButton4.setText(resourceMap.getString("jButton4.text"));
    jButton4.setName("jButton4");
  }

  @Action(block=Task.BlockingScope.COMPONENT)
  public Task searchNoBlocking() {
    searchTask = new SearchNoBlockingTask(Application.getInstance(MainApp.class));
    return searchTask;
  }

  private class SearchNoBlockingTask extends Task&lt;String, Void&gt; {
    StringBuilder str = null;
    Grep g = null;

    SearchNoBlockingTask(Application app) {
      super(app);
      jTextArea1.setText("");
      g = new Grep((jTextField2.getText()));
    }

    protected String doInBackground()  {
      try {
        if(f != null )
           g.searchInFiles(f);
      } catch (FileNotFoundException ex) {
        Logger.getLogger(MainPane.class.getName()).log(Level.SEVERE, null, ex);
      } catch (IOException ex) {
        Logger.getLogger(MainPane.class.getName()).log(Level.SEVERE, null, ex);
      }
      return g.getFormatedSearchResults();
    }
    protected void succeeded(String result) {
      getJTextArea1().setText(result);
    }
  }

  @Action
  public void cancel() {
    if(searchTask != null)
      searchTask.cancel(true);
  }
}
</pre>
<p>Tak jak wcześnie, do komponentu, w tym przypadku przycisku przypisujemy akcję, która tym razem nazywa się <code>searchNoBlocking</code> (linia 13.), inaczej wygląda za to implementacja wyszukiwania. Metoda obsługująca wyszukiwanie zwraca tym razem obiekt typu <code>Task</code>, którego instancję musimy utworzyć w metodzie (linia 25.).</p>
<p>Oczywiście nikt nam nie da gotowej klasy <code>Task</code>, także trzeba ją samodzielnie utworzyć, przesłaniając odpowiednie  metody &#8211; właśnie to jest naszym celem. Nasza implementacja <code>Task</code> nazywa się <code>SearchNoBlockingTask</code> (linia 31.) i przesłaniamy w niej trzy metody:</p>
<ul>
<li>konstruktor, w którym inicjalizujemy potrzebne obiekty, warto zwrócić uwagę na to, że możemy w nim modyfikować stan GUI</li>
<li><code>doInBackground()</code>, która jest odpowiedzialna za uruchomienie wyszukiwania &#8211; ta metoda działa w osobnym wątku, nie blokuje zatem interfejsu użytkownika, nie wolno w tej metodzie odwoływać się w związku z tym do elementów GUI. Metoda ta jest automatycznie uruchomiana, gdy tworzymy instancję klasy <code>SearchNoBlockingTask</code>.</li>
<li><code>succeeded</code>, która jest wywoływana jak tylko skończy się działanie metody <code>doInBackground()</code> &#8211; w tej metodzie możemy zaktualizować interfejs użytkownika przy pomocy danych uzyskanych z metody <code>doInBackground()</code>. Metoda ta jest oczywiście uruchomiona w wątku EDT, dlatego może modyfikować GUI.</li>
</ul>
<p>Klasa <code>SearchNoBlockingTask</code> ma jeszcze inne użyteczne metody, z jednej z nich korzystamy w metodzie <code>cancel</code>, umieszczonej na samym końcu listingu, przerywa ona wyszukiwanie po kliknięciu w odpowiedni przycisk.</p>
<p>Często się zdarza, że chcemy jednak zablokować jakąś część interfejsu użytkownika w czasie trwania długotrwałej operacji, możemy to zrobić ręcznie, w metodach klasy <code>SearchNoBlockingTask</code>, lub przekazując w parametrze metadanej <code>@Action</code> informację o tym, co chcemy blokować, w powyższym przykładzie blokujemy komponent, który spowodował rozpoczęcie wyszukiwania, czyli odpowiedni przycisk. Uchroni to naszą aplikację przed wielokrotnym jego naciśnięciem przez użytkownika.</p>
<p><strong>Co jeszcze potrafi SAF</strong><br />
Jest jeszcze kilka rzeczy, o których warto wspomnieć. SAF ułatwia tworzenie bardzo często pożądanej funkcjonalności, czyli paska postępu. SAF zapamiętuje także automatycznie stan GUI &#8211; wielkość i położenie okien (akurat w moim przykładzie to nie działa, gdyż jest on skonstruowany nie do końca zgodnie z duchem SAF)</p>
<p><strong>Dodatkowe informacje</strong></p>
<ul>
<li><a href="https://appframework.dev.java.net/">Strona projektu</a></li>
<li>Wywiad z Hansem Mullerem, twórcą SAF <a href="http://www.artima.com/lejava/articles/swingframework.html">A Framework for Swing</a></li>
<li>Wprowadzenie do tworzenia aplikacji SAF w NetBeans: <a href="http://www.netbeans.org/kb/60/java/gui-saf.html">Introduction to the Swing Application Framework</a></li>
<li>Dwa ciekawe artykuły poświęcone programowaniu z użyciem SAF: <a href="http://java.sun.com/developer/technicalArticles/javase/swingappfr/">Using the Swing Application Framework (JSR 296)</a> i<br />
<a href="http://weblogs.java.net/blog/diverson/archive/2007/04/swing_applicati.html">Swing Application Framework Hacks Unleashed For Smarty Pantses</a></li>
</ul>
<p><strong>Podsumowanie</strong><br />
Najważniejszy pytanie oczywiście brzmi, czy SAF się przyjmie. Niewątpliwie Swing potrzebuje tego typu rozwiązania, także coś tę pustkę wypełni i nie bardzo widać konkurencję na tym polu. Potrzebne jest też wsparcie narzędzi, na razie SAF wspiera tylko NetBeans i robi to całkiem dobrze, mimo kilku drobnych uciążliwości, jak na przykład notoryczne kasowanie nazwy komponentu, do którego dodajemy akcję. Zobaczymy, co się będzie działo na polu tworzenia interfejsu użytkownika, bo ewidentnie szykuje się mniejszy lub większy przełom. </p>
<p>Z jednej strony aplikacje &#8220;grubego klienta&#8221; przestają być wystarczające w niektórych zastosowaniach, z drugiej strony interfejs użytkownika, który można utworzyć przy pomocy HTML/CSS/JavaScript nawet jeśli wykorzystuje się AJAX jest daleki od tego, co byśmy chcieli dostać. Pytanie brzmi, jaka technologia będzie używana: może wraz z uproszczeniem instalacji Javy, która ma być wkrótce dostępna jako <a href="https://jdk6.dev.java.net/6u10ea.html">Java SE 6 Update 10 (zwany także 6uN)</a> do łask wrócą applety, które z technologicznego punktu widzenia są bardzo skutecznym rozwiązaniem w wielu zastosowaniach, podobnie może stać sie z Java Web Start. Są też oczywiście twardzi konkurenci na tym polu: <a href="http://www.adobe.com/products/flex/">Adobe Flex</a> i <a href="http://www.adobe.com/products/air/">Adobe Air </a>no i <a href="http://silverlight.net/">Silverlight</a> od Microsoftu. </p>
<p><em>Uwaga:</em> artykuł bazuje na dość wczesnej wersji SAF, która może się jeszcze zmieniać, także z czasem niektóre rzeczy mogą przestać działać, albo zacząć działać niepoprawnie.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xoft.pl/2008/05/13/aplikacja-w-swing-application-framework-prosta-ale-nie-za-prosta/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>IceFaces czy RichFaces: co wybrać?</title>
		<link>http://www.xoft.pl/2008/05/08/icefaces-czy-richfaces-co-wybrac/</link>
		<comments>http://www.xoft.pl/2008/05/08/icefaces-czy-richfaces-co-wybrac/#comments</comments>
		<pubDate>Thu, 08 May 2008 11:44:11 +0000</pubDate>
		<dc:creator>Piotr Kochański</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[JSF]]></category>

		<guid isPermaLink="false">http://www.xoft.pl/2008/05/08/icefaces-czy-richfaces-co-wybrac/</guid>
		<description><![CDATA[Wygląda na to, że są to dwaj najpopularniejsi gracze na rynku darmowych ajaksowych komponentów JSF, co zatem wybrać? Pierwsza rzecz: czy trzeba w ogóle wybierać? Niestety tak, obie biblioteki nie chcą ze sobą współpracować &#8211; każda z nich inaczej włącza się w cykl życia strony JSF i to powoduje różna zabawne problemy. Podobno w którejś [...]]]></description>
			<content:encoded><![CDATA[<p>Wygląda na to, że są to dwaj najpopularniejsi gracze na rynku darmowych ajaksowych komponentów JSF, co zatem wybrać? Pierwsza rzecz: czy trzeba w ogóle wybierać? Niestety tak, obie biblioteki nie chcą ze sobą współpracować &#8211; każda z nich inaczej włącza się w cykl życia strony JSF i to powoduje różna zabawne problemy. Podobno w którejś tam wersji ma być możliwa koegzystencja obu produktów&#8230; </p>
<p>Porównanie obu bibliotek można znaleźć na stronie <a href="http://www.jsfmatrix.net/">JSF Matrix</a>, ale niewiele z niego wynika, jak to zazwyczaj bywa ze statystykami w rodzaju &#8220;liczba dostępnych komponentów&#8221;.</p>
<p><strong>Kto i co robi</strong><br />
Zacznijmy od krótkiego przedstawienia obu bibliotek.</p>
<p><a href="http://www.jboss.org/jbossrichfaces/">RichFaces</a>. Biblioteka była produktem firmy Exadel, która połączyła się, albo została kupiona przez JBoss-a, czyli RedHata. Ehh, ten świat wielkich korporacji&#8230; Biblioteka jest dostępna na licencji Open Source, zawiera ponad 50 różnych komponentów JSF.</p>
<p><a href="http://www.icefaces.org">ICEFaces</a> jest robiona przez firmę ICESoft i też jest opensorsowa.</p>
<p><strong>Zalety RichFaces i wady ICE Faces</strong><br />
Dużą zaletą RichFaces jest pod-biblioteka Ajax4JSF, która pozwala &#8220;zajaksować&#8221; dowolny komponent JSF. Jest to bardzo wygodne, jeżeli mamy już napisany kawałek aplikacji i chcemy gdzieniegdzie ją upiększyć ajaksowymi wodotryskami.</p>
<p>Kolejną przyjemną cechą RichFaces jest możliwość określenia zachowania komponentów tak, żeby niekoniecznie za każdym razem łączyły się z serwerem (Ajaxa im można wyłączyć, innymi słowy). Można na przykład utworzyć stronę z zakładkami w ten sposób, że wszystkie są od razu ładowane i przy ich zmianie nie jest potrzebny kontakt z serwerem. Oczywiście można także skonfigurować sobie zachowanie ajaksowe, czyli zakładki będą doładowywane dynamicznie. </p>
<p>ICEFaces czegoś takiego nie potrafi, jego komponenty zawsze kontaktują się z serwerem, co jest trochę głupie w przypadku dajmy na to komponentu kalendarza &#8211; można go przecież zrobić łatwo po stronie klienta, każdorazowy kontakt z serwerem jest w 95% przypadków zbędny. Swoją drogą to jakaś obsesja, większość komponentów kalendarzowych ludzie robią ajaksem, tak przynajmniej mówi jsfmatrix.net.</p>
<p>ICEFaces pozwala używać JSF 1.2, ale wymaga używania trybu zgodności ze specyfikacją JSF 1.1. Trochę lipa.<br />
W <code>web.xml</code> trzeba deklarować użycie starej wersji JavaServelts<br />
<br/><code> &lt;!DOCTYPE web-app PUBLIC &quot;-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN&quot; &quot;http://java.sun.com/dtd/web-app_2_3.dtd&quot;&gt;</code><br/><br/> a w <code>faces-config.xml</code>: <br/><br/> <code>&lt;!DOCTYPE faces-config PUBLIC &quot;-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN&quot; &quot;http://java.sun.com/dtd/web-facesconfig_1_1.dtd&quot;&gt; </code></p>
<p>Przez to nie możemy wykorzystać różnych fajnych rzeczy, które wniosło JSF 1.2.</p>
<p>Łatwiej jest testować aplikacje, które używają RichFaces, bo <a href="http://www.jboss.org/jsfunit/">JSFUnit </a> wspiera właśnie to rozwiązanie &#8211; nic w tym dziwnego, gdyż JSFUnita robi RedHat.</p>
<p><strong>Zalety ICE Faces i wady RichFaces </strong><br />
Ok, wygląda na to, że RichFaces wygląda lepiej, ale parę rzeczy w ICE też mi przypadło do gustu. Przede wszystkim wygląd, komponenty są na prawdę dopracowane, wyglądają perfekcyjnie.</p>
<p>Druga rzecz, to sposób odświeżania strony JSF. ICE robią aktualizację wszystkich elementów ajaksowych na całej stronie, dzięki czemu nie musimy się martwić o synchronizację obsługi różnych żądań ajaksowych. W RichFaces trzeba to robić ręcznie, co ma swoje wady.</p>
<p>ICEFaces mają lepiej działającą obsługę ajaksa w trybie &#8220;push&#8221;, RichFaces też to potrafią, ale stosunkowo od niedawna, więc ciężko stwierdzić, na ile ich implementacja działa niezawodnie.</p>
<p>No i wreszcie na koniec ICEFaces dobrze integrują się nie tylko z Eclipse, ale także z NetBeansem (dla mnie to ważne, bo coraz częściej używam tego drugiego).</p>
<p><strong>Decyzja?</strong><br />
Póki co, zdecydowanie RichFaces.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xoft.pl/2008/05/08/icefaces-czy-richfaces-co-wybrac/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
