#21 — Wzorce projektowe

By 19 March 2015 Kurs Javy

Zapewne już nier­az spotkałaś się z określe­niem wzor­ców pro­jek­towych, dzisi­aj przy­bliżymy sobie czym są oraz jak je stosować (i jak ich nie stosować)

Wzorce pro­jek­towe są nieodłącznym przy­ja­cielem pro­gramisty — pozwala­ją pisać czyst­szy kod, łatwiejszy do zrozu­mienia przez innych i zapew­ni­a­ją pewien abstrak­cyjny zbiór rozwiązań abstrak­cyjnych prob­lemów. Wbrew częste­mu przeko­na­niu, nie są one gotowy­mi rozwiąza­ni­a­mi! Cytu­jąc p. Mar­ti­na Fowlera: “pat­terns are half-baked — mean­ing you always have to fin­ish them your­self and adapt them to your own envi­ron­ment“1 — to tylko półpro­duk­ty rozwiązania.

1) Mar­tin Fowler, http://martinfowler.com/ieeeSoftware/patterns.pdf

Lekcja

Wstęp

Mówiąc o wzor­cach pro­jek­towych nie wypa­da nie powiedzieć najpierw o ich de fac­to standaryza­cji, czyli kul­towej już książce “Design Pat­terns: Ele­ments of reusable Object-Ori­ent­ed Soft­ware”, którą wielu zna pod określe­niem ‘GoF Design Pat­terns” (GoF to skrót of Gang of Four — książ­ka ma czterech autorów). W pub­likacji tej nie tylko opisano najpop­u­larniejsze wzorce, pod­ję­to także  — udaną — próbę ustandary­zowa­nia opisu. Dzię­ki temu współcześnie różne wzorce i pomysły są opisane w podob­ny sposób, zaw­ier­a­jąc kluc­zowe infor­ma­c­je o każdym wzorcu:

  • Nazwa i klasy­fikac­ja wzorca
  • Cel — co pozwala osiągnąć
  • Motywac­ja — opisu­je cel wzor­ca (co stara się osiągnąć / jaki prob­lem wye­lim­i­nować) oraz powodu, dla których należy go użyć
  • Zas­tosowanie — opisu­je sytu­acje, prob­lem, który sugeru­je uży­cie wzorca
  • Struk­tu­ra — wiz­ual­na reprezen­tac­ja (najczęś­ciej w formie dia­gra­mu UML lub innego, koncepcyjnego)
  • Uczest­ni­cy — lista klas i obiek­tów, które biorą udzi­ał w imple­men­tacji tego wzorca
  • Współpra­ca — opis, w jaki sposób powyższe ele­men­ty współpracu­ją ze sobą, jakie są między nimi interakcje
  • Kon­sek­wenc­je — wynik uzy­cia wzor­ca, kosz­ty jego stosowa­nia i kompromisy
  • Imple­men­tac­ja — opis imple­men­tacji (np. w pseudokodzie lub nor­mal­nym tekstem)
  • Przykład — przykład imple­men­tacji w wybranym języku
  • Znane zas­tosowa­nia — przykłady uży­cia (np. w pop­u­larnych bib­liotekach, narzędziach
  • Pow­iązane wzorce — podob­ne lub kom­ple­men­tarne wzorce

Dodatkowo wyróż­ni­amy pod­sta­wowy podzi­ał wzor­ców na cztery kategorie:

  • Krea­cyjne opisu­ją, w jaki sposób obiek­ty są tworzone
  • Behaw­io­ralne opisu­ją zachowanie obiektów
  • Struk­tu­ralne opisu­ją sposób, w jaki obiek­ty są zbudowane
  • Architek­ton­iczne (wprowad­zone później) opisu­ją bardziej abstrak­cyjne wzorce jak np. MVC

Tem­at ten jest baard­zo sze­ro­ki i zapraszam do lek­tu­ry podlinkowanych mate­ri­ałów, książek i innych źródeł — warto przy­na­jm­niej prze­jrzeć, żeby w przyszłoś­ci kojarzyć i wiedzieć, gdzie wró­cić po infor­ma­c­je. Ostrzegam jed­nak przed mate­ri­ała­mi uczel­ni­any­mi (prezen­tac­je, sla­jdy) — nieste­ty, niek­tóre z nich są bard­zo nierzetelne, raczej zachę­cam do lek­tu­ry blogów o pro­gramowa­niu, architek­turze, pub­likacji poświę­conych tylko temu zagadnieniu.

W dzisiejszej lekcji jedynie poruszymy kil­ka wzor­ców, które wg nas są ważne na początku przy­gody z programowaniem.

Wzorzec: Fasada

Wzorzec fasady pole­ga na tym, że tworzymy klasę, której jedynym zadaniem jest wywoły­wanie odpowied­nich metod z innych klas (np. ser­wisów) cza­sem w odpowied­niej kole­jnoś­ci lub dodając/modyfikując pewne informacje.

Jako przykład (choć nie do koń­ca praw­idłowy, ale za to znany wszys­tkim czytel­niczkom) może­my podać kon­trol­ery w aplikacji webowej — najczęś­ciej ich dzi­ałanie ogranicza się do prostej logi­ki i wywoła­nia odpowied­niego ser­wisu (lub kilku serwisów).

W prak­tyce fasady częs­to spo­tykamy w sytu­ac­jach, kiedy mamy wiele różnych sys­temów (np. w kor­po­rac­jach), a potrze­bu­je­my spójnego sposobu na dostęp do danych z róznych źródeł (np. dla dzi­ału mar­ketingu potrze­bu­je­my danych ze sprzedaży oraz z mag­a­zynu). Fasa­da ułatwia dostęp do różnych obiek­tów i ukry­wa szczegóły implementacji.

Wzorzec: Fabryka

Wzorzec fab­ry­ka (ang. fac­to­ry method) to meto­da, która tworzy nam nowy obiekt. Powodów, dla których chce­my tak zro­bić może być wiele — najczęś­ciej jako typ zwracany deklaru­je­my inter­fe­js, a meto­da zaw­iera logikę która decy­du­je jakiego typu obiekt utworzyć:


public Zwierze rozpoznajZwierzaka(String dzwiek) {
    if (dzwiek.equals("hau")) {
        return new Pies();
    } else {
        return new Kot();
    }
}

Oczy­wiś­cie są przy­pad­ki, że logikę real­i­zowaną przez metodę-fab­rykę moglibyśmy zaszyć w kon­struk­torze, ale nie zawsze jest to wskazane. Takie pode­jś­cie pozwala na rozsz­erze­nie funkcjon­al­noś­ci w przyszłoś­ci za pomocą tech­nik OOP, np. dodanie obsłu­gi nowych klas czy zmi­ana algorytmu.
Przykład uży­cia fab­ry­ki to np. kon­fig­u­rac­ja Springa za pomocą kodu Javy (uży­wa­jąc metod z adno­tac­ja­mi @Bean).

(Anty)wzorzec: Singleton

Tutaj słówko wyjaśnienia (szczegól­nie dla osób z wyk­sz­tałce­niem IT, które były uczone tego wzor­ca jako pod­stawy). To nie wzorzec jest prob­le­mem, ale jego powszech­na per­cepc­ja i niezrozu­mie­nie. Nieste­ty wielokrot­nie prowadząc roz­mowy rekru­ta­cyjne na pytanie “jaki jest wg ciebie najważniejszy wzorzec pro­jek­towy” spo­tykałem się z odpowiedz­ią “Sin­gle­ton”, po czym po kole­jnym pyta­niu “ok, zatem podaj mi jego zas­tosowa­nia” nastawała cisza.

Sam wzorzec mówi o tym, że w sys­temie będzie uży­wany tylko jeden obiekt danego typu. Stan­dar­d­owa imple­men­tac­ja (“uczel­ni­ana”) wyglą­da następująco:


public class Singleton {
    private static Singleton instance = null;
    public static Singleton getInstance() {
        if (instance==null) {
            instance = new Singleton();
        }
        return instance;
    }

    private Singleton() {} //prywatny konstruktor
}

Prob­lem z takim pode­jś­ciem jest związany z tym, że więk­szość współczes­nych aplikacji jest wielowątkowa (np. aplikac­je webowe — dzieje się to częs­to zupełnie bez naszej wiedzy) i rozpros­zona, tego rodza­ju imple­men­tac­ja to prosty przepis na katas­trofę w kwestii wyda­jnoś­ci. Doda­jmy do tego abso­lut­ną niemożli­wość korzys­ta­nia z kon­cepcji OOP (np. rozsz­erze­nie / mody­fikacji sposobu dzi­ała­nia poprzez dziedz­icze­nie) Są inne możli­woś­ci real­iza­cji, które nadal może­my nazy­wać sin­gle­ton (idea zosta­je zachowana — mamy tylko jed­ną instancję, ale nie ‘wymusza­my’ tego, a raczej kon­fig­u­ru­je­my w ten sposób). Przykła­dem uży­cia tego wzor­ca jest Spring Frame­work — domyśl­nie każdy ele­ment, który oznaczymy jako @Service jest sin­gle­tonem (ale nie musi być — wiele rzeczy może spowodować, że tak nie będzie). Ważne żeby zapamię­tać, że sin­gle­ton jest nadal przy­dat­nym wzorcem, ale jego imple­men­tac­ja rodem z uczel­ni­anych sla­jdów ma swo­je miejsce na śmiet­niku his­torii i jed­nowątkowych aplikacji.

Podsumowanie

Po tej lekcji powin­naś mieć świado­mość, czym są wzorce oraz gdzie szukać infor­ma­cji na ich tem­at. W prak­tyce na początku częs­to będziesz się­gać do źródeł żeby poszukać pasu­jącego wzor­ca, z cza­sem więk­szość z tych uży­wanych na codzień wejdzie Ci w nawyk i nie będziesz nawet dostrze­gać, że ich uży­wasz :) Szczegól­nie pole­cam książ­ki wspom­ni­ane w treś­ci, warto mieć którąś z nich zawsze pod ręką.

To, co trze­ba koniecznie zapamię­tać (i wyryć w ścian­ie, względ­nie wydrukować i powiesić nad biurkiem) — wzorce pro­jek­towe nie są odpowiedz­ią na prob­le­my! Są wskazówka­mi dojś­cia do rozwiąza­nia, ale nie stanow­ią odpowiedzi. Moż­na je bard­zo łat­wo nadużyć. Abso­lut­nie niedo­puszczal­na jest sytu­ac­ja, w której ‘naciągamy’ prob­lem tak, aby pasował do konkret­nego wzor­ca (nie, ‘nawet troszeczkę’ też jest niedopuszczalne!).

Materiały dodatkowe / dokumentacja

  1. Lista wzor­ców na wikipedii (EN)
  2. Ama­zon: GOF Design Pat­terns (EN)

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przy­dat­ną, mamy małą prośbę: pol­ub nasz fan­page. Dzię­ki temu będziesz zawsze na bieżą­co z nowy­mi treś­ci­a­mi na blogu ( i oczy­wiś­cie, z nowy­mi częś­ci­a­mi kur­su Javy). Dzięki!