rss
    System.out.println("Hello World");
Se afișează postările cu eticheta factory. Afișați toate postările
Se afișează postările cu eticheta factory. Afișați toate postările

joi, 25 noiembrie 2010

Constrangeri, procese si specificatii in java: Partea IV Specificatii - constructia dupa plan a obiectelor

Uau! Put your hands in the air!
Suntem din nou aici cu specificatiile si cu toate lucrurile fascinante pe care ele ni le ofera!
Vreau sa va aud America, toti impreuna…
….

Ups! Stai ca scriem in romana… Hai frate...
....
E asta e!

Prezentam datile trecute cerintele pentru care ne luptam cu aceste specificatii.
Printre altele, este visul meu de la 3 ani (de cand mama ma obliga sa invat Luceafarul pe dinafara),
Sa pot construe obiecte in mod dynamic.
Mai précis, habar nu am la momentul rularii ce si cum trebuie sa arate un obiect, dar stiu ca am datele si regulile pe care acest obiect trebuie sa le indeplineasca, undeva, intr-un fisier, intr-o baza de date.
Pot specificatiile sa ma ajute in aest caz?
Ei bine nu, dar o pot face intr-un caz intermediar.
Dar daca dezvoltam putin conceptul de specificatie, vom obtine si acest comportament pur dinamic.
(intr-o postare ulterioara, poate il dezvoltam).

Despre constructia dupa plan a unui obiect!



Cand ne cumparam un nou televizor, ne uitam dupa anumite caracteristici.
Vreau ca televizorul sa aiba contrast cel putin 30000:1, sa aiba o diagonala de cel putin 66 cm, sa fie capabil sa decodeze si dvb-t si dvb-c, sa aiba gauri de conectare pentru cat mai multe tipuri de mufe, iar in final sa nu coste mai mult de 1300 ron.
O companie producatoare, poate lua aceste specificatii si imi poate livra mai multe sortimente de televizoare din gama lor care respecta aceste caracteristici.
Mai mult, companii competitoare, pot oferi produse conforme cu aceste specificatii.
Cuvantul cheie aici pare a fi „specificatii”.

La fel se intimpla si in viata computerelor.
Cand inserati o poza intr-un procesor de texte, textul curge pe langa poza.
Voi ati specificat locatia pozei si poate modul in care textul sa curga pe langa poza.
Din aceste specificatii, procesorul de texte va aranja liniile si cuvintele de asa natura incat sa se conformeze.
Desi nu e chiar vizibil la inceput, acest concept de specificatii, este fix acela pe care l-am folosit la validare si la selectie.
Desi implementarea va diferi putin, deoarece de data asta, nu e un filtru pentru obiecte existente, nu e un test pentru obiecte pentru a le testa integritatea, ci este o modalitate de a construi obiecte inexistente si de a le configura.

Multi dintre voi veti spune:
Bine, bine, de ce nu pot face o clasa, in care sa fac tot felul de metode, care sa constituie pasii constructiei?
Asa ar arata un generator de obiecte scris fara specificatii. Metodele respective ar defini comportamentul generatorului.

Insa, daca definim o interfata pentru generator, bazat pe specificatii, atunci aceasta ar constrange in mod explicit produsul generatorului, sa fie conform cu acea specificatie.
Aceasta abordare are cateva avantaje:
- Interfata generatorului este detasata de implementarea sa. Ea defineste, declara, ce lucruri vor trebui respectate, facute, fara a impune si cum aceste lucruri vor fi facute, lasand asta la latitudinea generatorului
- Interfata ofera posibilitatea ca programatorii sa inteleaga ce sa se astepte de la generator, fara ca acestia sa fie nevoiti sa inteleaga cum lucreaza generatorul de fapt. Singurele modalitati de a intelege rezultatele unui generator scris de o maniera procedurala, e prin rularea de diverse cazuri de test, ori prin intelegerea orcarei linii de cod.
- Interfata este mult mai flexibila, sau poate fi imbunatatita mult mai flexibil, deoarece documentul de cerinte este in mana clientilor (ei hotarasc cum si in ce fel va functiona softul), pe cand generatorul nu va trebui decat sa tina seama, sa respecte aceste specificatii.
- Aceasta interfata este mai usor de folosit si la testare, deoarece declara in mod explicit ce „input” trebuie dat generatorului, precum si un mod de a testa ceea ce iese din generator. Asa cum aminteam mai sus, specificatiile acestea pot fi folosite si in rolul lor de validare (daca implementarea o suporta (pentru asta se foloseste paternul assert despre care vom vorbi intr-o postare ulterioara)).


Dar unde este java?

Of, m-ati omorat cu aceasta intrebare! Vine, vine si java, e pe drum, i-auziti,
teleap-teleap!

Sa luam un exemplu al celor de mai sus. Nimic nu ne serveste mai concret decat un exemplu pedagogic.
Am fi putut scoate din programele noastre un exemplu, dar exemplele din viata reala tind sa fie mult prea complexe si cu detalii de implementare care omoara conceptul de baza.
Si de data aceasta, pentru a exemplifica ceea ce vrem sa spunem, Fovler si cu Evens ne sunt de mare ajutor.

Vom folosi exemplul oferit de Evens, in cartea sa.
Folosim aest exemplu, deoarece el ne-a ajutat si pe noi, in aplicatia noastra de facturi, comenzi si livrari.
Desi noi acolo vorbeam de alimente si bunuri de larg consum, nu de chimicale cum ne arata Evens in continuare.
Am folosit doar codul din acest exemplu, restul de comentarii, expliatii sunt ale noastre (adica ale mele, ce tot vorbesc eu asa la plural! Anunturicl dormi?)

Deci avem un program, care trebuie sa impacheteze cateva chimicale inofensive, dar foarte pretentioase la un loc.
Avem tnt care e explozibil, avem Amonium care e volatil, avem probe biologice (cum ar fi nisipu).
Si de parca nu ar fi de ajuns, mai exista si reguli care privesc combinatia dintre toate acestea.
Pe scurt regulile sunt asa:
Tnt nu poate sta decat in Container Blindat.
Amoniu nu poate sta decat in container ventilat.
Nisipu poate sta oriunde, dar nu poate fi in acelas container cu tnt-ul.

Vom scrie deci clase de specificatii pentru container.
Acestei specificatii de container, ii vom furniza caracteristicile de container.
Fiecare chimicala va avea o specificatie de container, configurata cu niste caracteristici de container.
Deci vom avea clasele: ContainerSpecification, ContainerFeature, precum si clasa Drum (chimicala).
Clasa ContainerSpecification va contine o caracteristica de container pe care containerul trebuie sa o indeplineasca.
Pentru a verifica acest lucru (dupa cum va amintiti de la validare) scriem o metoda.
Clasa va arata asa:

public class ContainerSpecification {
private ContainerFeature requiredFeature;
public ContainerSpecification(ContainerFeature required) {
requiredFeature = required;
}

boolean isSatisfiedBy(Container aContainer){
return aContainer.getFeatures().contains(requiredFeature);
}
}

Uitati cum vom construi specificatia unei chimicale:

tnt.setContainerSpecification(
new ContainerSpecification(ARMORED));

Am zis ca intr-un container sunt mai multe chimicale.
Va trebui deci sa il putem intreba pe container daca este impachetat in siguranta.
El va realiza acest lucru, intreband toate chimicalele din interiorul sau, daca el le satisface specificatia de container.
Cu alte cuvinte, cum un baiat intreaba o fata (sunt bun la pat iubit-o)?

Iata metoda din container care verifica acest lucru:

boolean isSafelyPacked(){
Iterator it = contents.iterator();
while (it.hasNext()) {
Drum drum = (Drum) it.next();
if (!drum.containerSpecification().isSatisfiedBy(this))
return false;
}
return true;
}


In acest moment am putea scrie foarte usor o bucata de cod care sa preia toate posibilitatile din baza de date sis a le verifice daca sunt bine ambulate:
Iterator it = containers.iterator();
while (it.hasNext()) {
Container container = (Container) it.next();
if (!container.isSafelyPacked())
unsafeContainers.add(container);
}

Dar nu pentru asta am fost platiti. Puteti sa le spuneti clientilor vostri ca, datorita inteligentei voastre, informatiilor gasite pe acest blog si cu bunavointa lui Fovler si Evens, puteti, folosind domain driven design, sa scrieti usor orice noua cerinta, dar noi vroiam sa generam obiecte.
Am vazut insa, cat de usor e sa implementezi si partea de validare daca doresti.

Revenim la cazul nostrum, trebuie sa scriem un soft, care preia niste chimicale, niste containere si optimizeaza impachetarea acestora.
Vom scrie o interfata pentru un serviciu care sa faca asta.
Pentru ca mai intai ne-am lamurit cum e cu partea de test a impachetarii, acum ne va fi foarte usor sa facem asta:

public interface WarehousePacker {
public void pack(Collection containersToFill,
Collection drumsToPack) throws NoAnswerFoundException;

/* Cerinta: La finalul metodei pack(), ContainerSpecification
Al fiecarui Drum trebuie sa fie satisfacuta de al sau Container.
Daca o solutie completa nu poate fi gasita, se va arunca o exceptie */

}

In acest moment, implementarea modului de impachetare a fost detasata de restul design-ului, ne incarcandu-l pe acesta.
Dar atentie, regulile care definesc domeniul de activitate, au ramas incluse in modelul de proiectare.

Vom vorbi putin aici si de lucrul in echipa.
Adesea se intimpla ca echipe de programatori sa lucreze la portiuni de aplicatii in parallel, dar acele portiuni de aplicatii sa fie interdependente, astfel incat, la un moment dat, in cazul unui design prost, una, sau mai rau, ambele echipe vor stagna.
Sa luam acest exemplu.
A scrie un soft de optimiare, de orice fel ar fi el, este o provocare mareata, desigur daca vorbim de un optimizator pe bune sin u unul cu numele.
Inchipuitiva ca avem o echipa incercand sa dezvolte serviciul de impachetare, iar alta echipa incercand
Sa scrie restul de cod.
Cea de a doua echipa, va scrie codul de integrare in baza de date, scoaterea de date de acolo, introducerea lor in serviciul de impachetare, preluarea rezultatelor de la acesta, interpretarea si afisarea lor.
Ce ne facem insa, ca ei nu pot face decat oleaca de integrare cu baza de date, pot eventual sa faca designul la o interfata grafica, dar cam atit.
Nici macar nu pot sa arate un prototip utilizatorilor sis a aiba un feetback (aferentatie inversa in romana! Eeee! Va place ce cuvinte stiu?), de la ei despre functionarea softului,
Deoarece acest feetback nu ar fi valabil decat in cazul unui system functional de la cap la coada.
Dar stati putin, ca cu design-ul prezentat mai sus, aceasta a doua echipa, poate scrie repede un optimizator nu tocmai performant, dar sufficient pentru a prezenta softul clientilor, iar la momentul cand prima echipa a terminat marele optimizator performant, acesta doar sa fie introdus in soft.
Cum arata codul echipei a doua deci:

public class Container {
private double capacity;
private Set contents; //Drums

public boolean hasSpaceFor(Drum aDrum) {
return remainingSpace() >= aDrum.getSize();
}

public double remainingSpace() {
double totalContentSize = 0.0;
Iterator it = contents.iterator();
while (it.hasNext()) {
Drum aDrum = (Drum) it.next();
totalContentSize = totalContentSize + aDrum.getSize();
}
return capacity – totalContentSize;
}

public boolean canAccommodate(Drum aDrum) {
return hasSpaceFor(aDrum) &&
aDrum.getContainerSpecification().isSatisfiedBy(this);
}

}


public class PrototypePacker implements WarehousePacker {

public void pack(Collection containers, Collection drums)
throws NoAnswerFoundException {

/* Aceasta metoda satisface cerinta asa cum a fost ea scrisa in interfata. Totusi, cand are loc o exceptie, este posibil ca datele continute in containere sa se fi schimbat. Activitatea de rollback trebuie realizata la un nivel mai sus. */

Iterator it = drums.iterator();
while (it.hasNext()) {
Drum drum = (Drum) it.next();
Container container =
findContainerFor(containers, drum);
container.add(drum);
}
}
public Container findContainerFor(
Collection containers, Drum drum)
throws NoAnswerFoundException {
Iterator it = containers.iterator();
while (it.hasNext()) {
Container container = (Container) it.next();
if (container.canAccommodate(drum))
return container;
}
throw new NoAnswerFoundException();
}

}

Si cu aceasta am exemplificat si a treia caracteristica a specificatiilor. Lucrurile pot fi dezvoltate de aici mult mai mult, dar asta depinde si de timpul si de bugetul si de cheful pe care il aveti.
Aveti insa, prin amabilitatea ;lui Evens si Fovler si din experienta noastra cu acest concept, o modalitate corecta si completa de a modela anumite lucruri, astfel incat san u mai injurati ori de cate ori un client va cere un lucru nou.
Dupa cum s-a vazut intr-un soft bine modelat, lucrurile chiar grele se adauga relative usor.
Va multumesc ca ati urmarit acest serial de 4 zile, iar maine va invite la un vot pe care AnunturiCL il va realize.
In functie de dorinta dumneavoastra,
Vorbim despre un subiect sau altul, sau nu mai vorbim de loc!
Multumiri avem si pentru:
Fovler si Evens pentru munca depusa in domeniul software developing, pentru ca ne-au scos pe noi la momentul potrivit din rahat cu gandirea lor extraordinara si ca au pus la dispozitia lumii intregi, cunostintele lor.


In elaborarea acestui articol ne-am sprijinit pe:

Domain-driven design : tackling complexity in the heart of software / Eric
Evans.
p. cm.
Includes bibliographical references and index.
ISBN 0-321-12521-5

Evans, E., and M. Fowler. 1997. "Specifications." Proceedings of PLoP 97 Conference.

miercuri, 24 noiembrie 2010

Constrangeri, procese si specificatii in java: Partea III Specificatii - Selectia

Atacam astazi a doua cerinta pentru specificatii, selectia cu toate problemele sale.


Despre selectie!

Aici avem doua sub probleme.
Le vom ataca pe rand.
Pentru simplitate vom presupune mai intai ca toate datele le avem in memorie si ca nu avem nevoie de date din baza de date.
Ne referim la acelas exemplu cu facturile.
Vrem sa obtinem toate facturile „delicvente”:


public Set selectSatisfying(InvoiceSpecification spec) {

Set results = new HashSet();
Iterator it = invoices.iterator();
while (it.hasNext()) {
Invoice candidate = (Invoice) it.next();
if (spec.isSatisfiedBy(candidate)) results.add(candidate);
}

return results;
}

Sa zicem ca aceasta metoda se afla in repository de facturi (InvoiceRepository).
Un client va putea obtine deci toate aceste facturi, bazandu-se pe o specificatie:

Set delinquentInvoices = invoiceRepository.selectSatisfying(
new DelinquentInvoiceSpecification(currentDate));

Din pacate, este foarte rar cazul cand avem toate datele in memorie, iar interactiunea cu baza de date, desi pare simpla pentru unii, este cel mai greu de obtinut in conditii corecte.
Este cu atit mai greu cu cat, ne dorim obtinerea unui model detasabil fata de baza de date si in care interogarile sql san u apara la fiecare pas.
Vom prezenta in continuare, pasi prin care am trecut cu rationamentul pentru a ajunge la codul perfect.
In continuare, de ajutor ne-au fost (prin scrierile lor Fovler si Evens).
Mai intai, ca multi dintre voi, ne-am gandit sa bagam in clasa specificatie o metoda ca urmatoarea:

public String asSQL() {
return
"SELECT * FROM INVOICE, CUSTOMER" +
" WHERE INVOICE.CUST_ID = CUSTOMER.ID" +
" AND INVOICE.DUE_DATE + CUSTOMER.GRACE_PERIOD" +
" < " + SQLUtility.dateAsSQL(currentDate);
}

Cum ziceam, acest lucru implica insa o problema, o sa incepem sa avem cod sql peste tot in aplicatie si asta nu ar fi asa o problema, daca nu ar depinde acest sql de structura tabelelor din baza de date.
Regula generala:
Informatia despre baza de date si structura ei, trebuie bine izolata intr-un strat (layer) de legatura intre aplicatie si baza de date sin u trebuie sa iasa de acolo.
Altfel, vom avea probleme de intretinere a aplicatiei.
S
Solutia numarul doi, este sa urmam regula de mai sus, vom pune interogarea in InvoiceRepository, formand o Metoda de interogare specializata, darn u vom furniza toate detaliile reguli de verificat, pentru a nu introduce detalii despre aplicatie in stratul (layer) legat de baza de date.
Ci vom divide funtionalitatea intre cele doua straturi si vom folosi un mechanism de chemare dubla (double dispatch) pentru a realize verificarea regulii.
(despre patterni de dispatch si dispatchers, vom vorbi intr-o alta postare).

public class InvoiceRepository {

public Set selectWhereGracePeriodPast(Date aDate){
String sql = whereGracePeriodPast_SQL(aDate);
ResultSet queryResultSet =
SQLDatabaseInterface.instance().executeQuery(sql);
return buildInvoicesFromResultSet(queryResultSet);
}

public String whereGracePeriodPast_SQL(Date aDate) {
return
"SELECT * FROM INVOICE, CUSTOMER" +
" WHERE INVOICE.CUST_ID = CUSTOMER.ID" +
" AND INVOICE.DUE_DATE + CUSTOMER.GRACE_PERIOD" +
" < " + SQLUtility.dateAsSQL(aDate);
}

public Set selectSatisfying(InvoiceSpecification spec) {
return spec.satisfyingElementsFrom(this);
}
}

public class DelinquentInvoiceSpecification {

public Set satisfyingElementsFrom(
InvoiceRepository repository) {
return repository.selectWhereGracePeriodPast(currentDate);
}
}

Aceasta varianta, aseaza interogarea in repository, in timp ce specificatia controleaza ce interogare va fi folosita.
Chiar daca regula nu e toata in specificatie, essential este ca in specificatie se defineste clar ce inseamna “delicventa” unei facturi si anume depasirea perioadei de gratie.

Repository are acum o metoda de interogare foarte specializata, care probabil va fi folosita doar in cazul “delicventei”.
Asta este acceptabil, dar gandindu-ne la numarul de facturi care doar au deposit perioada de scadenta, fata de cele “delicvente”, ajungem la o solutie intermediara, care face metodele din repository mai generale, lasand specificatia sa fie mai concreta.

public class InvoiceRepository {

public Set selectWhereDueDateIsBefore(Date aDate) {
String sql = whereDueDateIsBefore_SQL(aDate);
ResultSet queryResultSet =
SQLDatabaseInterface.instance().executeQuery(sql);
return buildInvoicesFromResultSet(queryResultSet);
}

public String whereDueDateIsBefore_SQL(Date aDate) {
return
"SELECT * FROM INVOICE" +
" WHERE INVOICE.DUE_DATE" +
" < " + SQLUtility.dateAsSQL(aDate);
}

public Set selectSatisfying(InvoiceSpecification spec) {
return spec.satisfyingElementsFrom(this);
}
}

public class DelinquentInvoiceSpecification {
//alt cod.

public Set satisfyingElementsFrom(
InvoiceRepository repository) {
Collection pastDueInvoices =
repository.selectWhereDueDateIsBefore(currentDate);

Set delinquentInvoices = new HashSet();
Iterator it = pastDueInvoices.iterator();
while (it.hasNext()) {
Invoice anInvoice = (Invoice) it.next();
if (this.isSatisfiedBy(anInvoice))
delinquentInvoices.add(anInvoice);
}
return delinquentInvoices;
}
}


In speranta ca sunteti multumiti de cat java ati vazut, chit ca discutam incontinuare despre business layer, va spun ca urmeaza cea mai interesanta parte,
Aceea a constructiei obiectelor dupa un plan bine stabilit, intr-un mod declarativ, fara prea multe if-uri thenuri.
Practic urmeaza un generator de obiecte pe care il voi prezenta.
Deci nu pierdeti episodul de maine!

Va urma

In elaborarea acestui articol ne-am sprijinit pe:

Domain-driven design : tackling complexity in the heart of software / Eric
Evans.
p. cm.
Includes bibliographical references and index.
ISBN 0-321-12521-5

Evans, E., and M. Fowler. 1997. "Specifications." Proceedings of PLoP 97 Conference.