31 mar 2009

To zależy...

Rozwijanie oprogramowania to zajęcie inheretnie intelektualne. Taki truizm mi się nasunął :)
Ale zdaje mi się, że wielu programistów o tym zapomina. Myślę, że są ku temu dwa powody - przyzwyczajenia i błędne założenia.
Klasyczny wodospadowy model produkcji oprogramowania zakłada, że najpierw tworzona jest architektura i projekt oprogramowania (i tu jest myślenie - od tego jest Architekt i Projektant, którzy za pomocą Drogiego Narzędzia CASE/UML tworzą Architekturę) a potem trzeba to po prostu zakodować. Tak jak z budynkiem, tylko cegły się ciężej układa, więc przydadzą się studia. I programiści w jakimś stopniu to kupują i programują tak jak im się akurat przydarzy.
Ale w wielu firmach, szczególnie mniejszych software house'ach nie ma działu architektury. Tam ludzie przyzwyczajeni są - jeszcze może z czasów studiów - że myśli to się o algorytmach, o drzewach czerwono-czarnych, o ray-tracingu, może nawet o szeregowaniu zadań w systemie operacyjnym, ale przecież nie w aplikacji webowej. Tę każdy wie jak napisać - tu nie ma o czym myśleć.
Programiści oczekują gotowych rozwiązań. Że książka, blog, prezentacja dadzą im konkretne odpowiedzi. Że zasugerowane w czasie wykładu, rozmowy wzorce będą dobre na każdą sytuację.

Ale oprogramowanie to nie budynki (choć nic nie wiem o tym, może tam też trzeba myśleć...) Tu odpowiedzi są ogólne i niepewne. Bez drobiazgowej znajomości wymagań i reszty aplikacji nie sposób dać stu procentowej odpowiedzi. Nad każdym szczegółem, najdrobniejszym detalem powinno się posiedzieć. Przemyśleć. Zbadać siły działające w tej sytuacji. Przemyśleć jak dany kod będzie wykorzystywany. Ani reguły SOLID ani wzorce GoF ani jakieś cechy języka nie zastąpią myślenia.
Czasem trzeba brać pod uwagę kwestie merytoryczne - wydajność jakiegoś rozwiązania, bezpieczeństwo, itp. Czasem percepcyjne - jak zorganizowany kod jest czytelniejszy, co łatwiej jest zrozumieć. Czasem w końcu organizacyjno-psychologiczne - że baza danych zależna jest od kogo innego i często się zmienia, że większość zespołu woli jakiś inny sposób formatowania, itp.
Dlatego właśnie Big Design Up Front nie może dać dobrej aplikacji (tzn. może się komuś uda, ale generalnie...) Bo nie da się bez kodu i zespołu przemyśleć tego wszystkiego odpowiednio dokładnie.

Zapytał mnie niedawno kolega z pracy jakie rozwiązanie jest lepsze (zainspirowany moim ostatnim wpisem) - mieć oddzielne DAO czy metody operacji na danych bezpośrednio w encjach (Active Record). I nie ma tu dobrej odpowiedzi. W jakimś stopniu jest to kwestia estetyki, w jakimś designu reszty aplikacji. Może tez wchodzić w grę doświadczenie programistów, wybór technologii do przechowywania danych itp. itd.
Czasem używanie tych samych obiektów jako dziedziny (wraz z zachowaniami) i reprezentantów bazy danych (ORM, DTO) jest ok, a czasem (no dobra, częściej) warto trzymać swoje obiekty oddzielnie i nie wystawiać wszystkich ich pól do publicznego dostępu (w ten sposób wiążąc implementacje innych elementów kodu z wyłącznie naszą implementacją dziedziny) a dla Hibernate'a wystawiać DTO (inna reprezentacja danych w bazie a inna w dziedzinie, zmieniająca się struktura bazy, itp.)

Na wiele pytań nie ma jednoznacznych odpowiedzi na samym początku. Na wiele nie ma ich też później, dlatego robimy oprogramowanie tak, by na bieżąco dostosowywać je (refaktoryzować) do zmieniających się warunków (a przecież warunki się ciągle zmieniają w czasie developmentu, bo dodawane są kolejne funkcjonalności). Oprogramowanie nie jest z kamienia ani żelbetu, tylko jest realizacją naszych (elastycznych) myśli.
Tylko od Ciebie zależy jak je budujesz.

19 mar 2009

Repozytoria w DDD

W czasie prezentacji o DDD (albo zaraz potem) zadał mi ktoś pytanie o realizację Repozytoriów w javie. Jeśli traktować Repozytoria jako klasyczny wzorzec DAO, to realizacja jest prosta. Robimy generyczne DAO, który obsługuje wszystkie zwyczajne przypadki (CRUD) a tam, gdzie potrzebujemy jakichś szczególnych (bardziej biznesowych) metod, doimplementowujemy to w klasie dziedziczącej po tymże DAO. DAO można zrobić np. tak jak w przykładzie na stronach Hibernate. To rozwiązanie niestety wymaga podawania explicite tego DAO, wszczepiania go tam gdzie chcemy go użyć itp. Oddzielność tej warstwy jest ewidentna (można dyskutować czy to dobrze czy to źle, ale to głównie estetyka). Niektórzy wolą więc zarządzanie obiektami dziedziny w sposób a'la Grails/GORM:
public class RepositoryInsideDomain {

@Test
public void checkSaveFindDelete () {
Author author = new Author();
author.setName("Pawel");

author.save();
assertTrue(Author.isStored(author));

assertNotNull(Author.findById("Pawel"));
assertEquals(author, Author.findById("Pawel"));

author.delete();
assertFalse(Author.isStored(author));
}
}
Co niestety wymaga albo pobrudzenia obiektów dziedziny dodaniem odpowiednich metod z DAO i delegacji do nich (trywialne, nieutrzymywalne i nieładne, więc nawet nie będę pokazywał, bo ktoś powie że to promuję :)),  albo pobawieniem się aspektami. I to rozwiązanie może być ciekawe, więc je tu pokażę (zrealizowane za pomocą aspectj'a). Do tego potrzebujemy zrobić Repozytorium (tu strywializowane, ale oczywiście może być dowolnie bardziej złożone):
public class Repository {
static Map storage = new HashMap();

public Repository() {
}

public static Object findById (Object id) {
return storage.get(id);
}

public void save () {
storage.put(this.toString(), this);
}

public void delete () {
storage.remove(this.toString());
}

public static boolean isStored (Object o) {
return storage.containsKey(o.toString());
}
}
i jeszcze aspekt który pomiksuje to wszystko razem:
public aspect RepositoryInjector {
declare parents: com.lipinski.domaininjection.domain.* extends Repository;
}
Tak niewiele trzeba, by odpowiednie metody repozytorium były dostępne bezpośrednio z klas dziedziny. Dzięki temu nie musimy wszczepiać nigdzie repozytoriów, operujemy wyłącznie na klasach domenowych. Z drugiej strony na poziomie kodu mamy ładną separację dziedziny od infrastruktury. jedyna wada tego rozwiązania, to że nie jest to pure java, i póki się nie zajrzy do tego aspektu to nijak nie wiadomo skąd te metody się znalazły. Ale jeśli to podejście stosujemy konsekwentnie w całej aplikacji, to nie powinien być problem.

18 mar 2009

Po speechu z DDD

Na wczorajszym speechu z DDD w Warszawkim JUGu zgromadziło się 70 osób, co wg. organizatorów jest dość dużo (mi też się tak wydaje...) To bardzo cieszy, bo wskazuje na to, że jednak może kryzys w oprogramowaniu nie jest aż tak wielki, albo przynajmniej, że jest trochę ludzi, którzy chcą pisać dobry soft. Którym zależy na jakości.

Wszystkim obecnym chciałem podziękować za miłe przyjęcie. Fajnie jest widzieć zainteresowanie i zaangażowanie.

Prezentacja dla chętnych do ponownego obejrzenia dostępna jest na google docs.

Do tego jest dość aktywny wątek poprezentacyjny na stronach JUGa: tu.

16 mar 2009

Manifest Rzemieślnika Oprogramowania


W czasie gdy ja w miarę bezrefleksyjnie oddawałem się swojej drugiej pasji (na obrazku), powstał appendix do manifestu agile. Oryginalny manifest mówił o tym na co w zwinnym podejściu do rozwoju oprogramowania kładziony jest głównie nacisk, a co jest 'tylko' wartościowe. Były to: 
Osoby i Interakcje nad Procesy i Narzędzia
Działające Oprogramowanie nad Szczegółową Dokumentację 
Współpraca z Klientem nad Negocjacjowanie Kontraktu
Odpowiadanie na Zmiany nad Działanie Zgodnie z Planem.

Robert C. Martin już od jakiegoś czasu postulował dodanie jeszcze czegoś, co definiowało by podejście do jakości w projektach/zespołach agile'owych. Sam nazwał to w zeszłym roku 'Craftsmanship over Crap', ale zdaje się, że to jednak nie pasuje (w końcu elementy po prawej miały też przedstawiać jakąś wartość, tyle że mniejszą...) i nie brzmi zbyt poprawnie politycznie. Stąd twórcy appendixu zwanego Manifesto for Software Craftsmanship ubrali różne myśli krążące od pewnego czasu po książkach i blogach agile'owych w ładne słowa i powstało:
Nie tylko Działające Oprogramowanie, ale również Niezła Robota (moje domorosłe tłumaczenie 'well-crafted')
Nie tylko Odpowiadanie na Zmiany, ale również Ciągłe Dodawanie Wartości
Nie tylko Osoby i Interakcje, ale również Społeczność Zawodowców
Nie tylko Współpraca z Klientem, ale również Produktywne Partnerstwa

Manifest mówi, że do osiągnięcia elementów po lewej niezbędne jest istnienie tych po prawej. No więc po kolei:
Żeby oprogramowanie Działało, spełniało swoje zadania i nie wymagało Szczegółowej Dokumentacji, musi być to Niezła Robota - wysokiej jakości oprogramowanie.
Żeby móc odpowiadać na zmiany, trzeba móc ciągle dodawać wartość. A do tego warunkiem sine qua non jest kod łatwo modyfikowalny, o czystej architekturze, zrozumiały.
Osoby odpowiedzialne za projekt tworzą się, kształcą poprzez interakcję z innymi. Z lepszymi od siebie, ale ze słabszymi od siebie również. Do tego niezbędna jest społeczność osób związanych ze sobą zainteresowaniami, pracą, pasją. Stąd potrzeba społeczności profesjonalistów.
W końcu produktywne partnerstwo jako efekt ale i warunek dobrej współpracy. Jeśli firmy szanują się nawzajem, jeśli ufają swojemu profesjonalizmowi i mają jasne, przezroczyste reguły współpracy, takie partnerstwo (czyli równość stron) może być produktywne. Obie firmy mogą osiągnąć swój cel - wzajemne przynoszenie sobie wartości.

Ten manifest, zupełnie jak ten oryginalny, jest idealistyczny. Zakłada dobre chęci, profesjonalizm, budowanie wzajemnego zaufania. To nie środki, ale cel. Coś do czego powinniśmy dążyć całym sobą, ucząc się ciągle i poprawiając. Na początek na naszym małym podwórku jednego projektu, jednego zespołu, jednej aplikacji. Z czasem może uda nam się zarazić tym innych, firmę, społeczność. To dzięki takim właśnie postawom mamy dziś cały ruch agile.

5 mar 2009

Speech z DDD

Tym razem trochę czystej prywaty. W następny wtorek, 17 marca, zapraszam na mój speech w ramach Warszawskiego JUGa. Tematem będzie Domain Driven Design. Oczywiście nie obędzie się bez ewangelizacji zwinnego podejścia do tworzenia projektów, czyli po prostu edżajla.

Kto nie będzie mógł przyjść, będzie mógł obejrzeć sobie slajdy, ale jak mawiała moja nauczycielka Polskiego "Lipiński, lepiej gadasz niż piszesz", więc pogadanka będzie pewnie bardziej wartościowa niż suchy PDF.

2 mar 2009

SOLID - Liskov Substitution Principle

Pisanie naprawdę obiektowego kodu nie jest popularne. Tak naprawdę nie widziałem zbyt wielu projektów pisanych obiektowo. A w javie piszę już 10 lat. W tzw. aplikacjach enterprise nie ma prawie dziedziczenia, nie ma hierarchii. Aplikacje są totalnie płaskie, proceduralne, z niejasną strukturą, z czasem bardzo trudne do utrzymania.

Wartość dziedziczenia przy strukturyzowaniu kodu pokazywałem już w poprzednich postach, w szczególności przy okazji zasady zamknięcia-otwartości. Natomiast zasada o której teraz trochę napiszę dotyczy nie tego jak ma wyglądać struktura, ale co ma być w klasach dziedziczących. Zasada podstawiania Liskov brzmi mniej-więcej tak:
Jeśli twój kod oczekuje jakiejś klasy, to zamiast niej powinieneś móc podstawić dowolną klasę z niej dziedziczącą bez zmieniania żadnych oczekiwanych zachowań.
Oznacza to tyle, że jeśli przeciążamy jakąś metodę w podklasie, to musi ona zachować semantykę odpowiedniej metody nadklasy.

Ta zasada mówi jedną bardzo ważną rzecz: jeśli przeciążasz metodę, napisz ją tak, by użyta polimorficznie działała poprawnie. Wyobraźmy sobie taką sytuację: mamy aplikację składającą się z modułów. Są tam zwykli użytkownicy oraz administratorzy modułów. Mamy użytkownika:


public class User {
protected String login;
protected String password;
protected List modules;

public void addAccessToModule(Module module) {
modules.add(module);
}

public boolean canAccess(Module module) {
if (modules.contains(module))
return true;
return false;
}
}

Mamy też administratora:

public class Admin extends User {
private List administeredModules;

public boolean canAccess(Module module) {
if (administeredModules.contains(module))
return true;
return false;
}
}


Ja widzimy metoda canAccess jest przeciążona w klasie Admin i sprawdza czy dany użytkownik jest administratorem podanego modułu (programista zaimplementował w ten sposób wymaganie, że administrator ma dostęp do wszystkich modułów jako zwykły użytkownik, a jako administrator do pewnego ich podzbioru). Wyobraźmy też sobie, że moduły emitują zdarzenia, na których otrzymywanie użytkownicy mogą się zapisać:

public void subscribeToEvents(User user, Module module) {
if (! user.canAccess(module)) {
user.addAccessToModule(module);
}
module.sendEventsTo(user);
}

Na pierwszy rzut oka nie widać żadnego błędu - jeśli ktoś jeszcze nie ma dostępu do jakiegoś modułu, to jest on mu przyznawany i dopisywany jest on do listy użytkowników notyfikowanych o zdarzeniach. Ale niestety przez to, że programista nie zastosował się do zasady podstawiania, kod ten jest poprawny tylko dla zwykłych użytkowników. Wielokrotne wywoływanie tej metody dla obiektów klasy Admin spowoduje ciągłe dodawanie tych samych modułów do do kolekcji modules klasy User, choć metoda canAccess w klasie Admin w ogóle jej nie sprawdza.

Nie zachowywanie zasady podstawiania Liskov powoduje powstawanie błędów często bardzo trudnych do wykrycia. Błąd jest prawie zawsze w klasie, której w kodzie wołającym w ogóle nie widać - w końcu są to wywołania polimorficzne. Przy bogatszych hierarchiach i duplikacji w kodzie szukanie błędów może się zmienić w wielogodzinną walkę. Co więcej, jeśli nie dbamy o jakość naszego kodu (właściwe hierarchie, duplikacje, właściwe umiejscowienie metod), nawet szczegółowe testy jednostkowe ze 100% pokryciem kodu może nic nie pomóc (w powyższym przykładzie wystarczy, by był jeszcze jakiś inny sposób na dodanie modułu do kolekcji modules, by testy jednostkowe nie wychwyciły tego problemu).