rss
    System.out.println("Hello World");

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.

Niciun comentariu:

Trimiteți un comentariu

Va rugam lasati un comentariu!