Wzorce logiki dziedziny 1. Wzorce logiki dziedziny skrypt transakcji (Transaction Script), brama tabeli (Table Data Gateway), model dziedziny (Domain model), strategia (Strategy), moduł tabeli (Table Module), zbiór rekordów (Record Set). 4. Warstwa usług Domain Facade, Operation Scripts, Layer Supertype, Session Facade. 1
Transaction Script Pojedyncza interakcja pomiędzy programem klienckim a aplikacja biznesową zwykle powoduje (zgodnie z logiką aplikacji) wykonanie szeregu operacji transakcji. Wzorzec Transaction Script implementuje je w pojedynczej procedurze. Przykład: Procedura rezerwacji kabiny: - utworzenie nowej rezerwacji w systemie, - obciążenie karty kredytowej, - wydrukowanie biletu. 2
Przykład Sprzedaż ratalna: Product type 1 N 1 N Contract signed total Installment amount date Z każdą umową związany jest jeden rodzaj produktów. Każda wpłata dotyczy określonej umowy. Chcemy: - znaleźć przychód na dany dzień wynikający z określonej umowy, - tworzyć harmonogramy spłat. 3
Table Data Gateway class InstallmentGateway... public ResultSet finfinstallmentfor(long cid, date d){ PreparedStatement stmt = db.preparestatement( "SELECT amount FROM installment WHERE" + " con_id =? AND date <=?"); stmt.setlong(1, cid); stmt.setdate(2, d); return stmt.executequery(); Wzorzec Gateway służy do oddzielenia kodu SQL od reszty aplikacji. Zwykle dla jednej tabeli tworzona jest jedna klasa typu Gateway. 4
Transaction Script class InstallmentService... public Money getincome(long cid, Date d){ Money m = new Money(0.0); try{ ResultSet rs = db.findinstallmentfor(cid, d) while(rs.next()){ m.add(new Money(rs.getBigDecimal("amount"))) return m; catch(sqlexception ex){ throw new ApplicationException(ex); Skrypt sumuje dane przekazane przez bramę zawiera logikę biznesową. 5
Transaction Script Analogicznie realizujemy tworzenie harmonogramy spłat: class InstallmentService... public void calculateinstallments(long cid){ try{ ResultSet rs = db.findcontract(cid); rs.next(); Date d = rs.getdate("signed"); Money total = rs.getbigdecimal("total"); String type = rs.getstring("type"); if (type.equals("a")){ Money inst = total.allocate(3); db.insertintallment(cid, inst[0], d); db.insertintallment(cid, inst[1], d.addmonth(1)); db.insertintallment(cid, inst[2], d.addmonth(2)); else if (type.equals("b")){ Money inst = total.allocate(2); db.insertintallment(cid, inst[0], d.addmonth(6)); db.insertintallment(cid, inst[1], d.addmonth(12));... catch(sqlexception ex){... 6
Transaction Script InstallmentService getincome(long, Date): Money calculateinstallments(long): void Zalety: - prostota w przypadku niewielkich aplikacji, - efektywność Wady: - powtórzenia kodu, 7
Domain Model Przychód na dany dzień wynikający z określonej umowy jest obliczany w klasie Contract.... class Contract... private List<Installment> installments;... public Money getincome(date d){ Money m = new Money(0.0); for (Iterator<Installment> it = installments.iterator(); it.hasnext(); ){ Installment inst = it.next(); if (inst.getdate().before(d)){ m.add(inst.getamount()); return m; 8
Domain Model Tworzenie odpowiednich obiektów Installment jest realizowane przy współpracy kilku klas. class Contract... private Product product; private Money total; private Date signed;... public Contract(Product p, Modey m, Date d){ this.product = p; this.total = m; this.signed = d; 9
Domain Model class Product... private InstallmentStrategy strategy;... public Product(..., InstallmentStrategy s){... this.strategy = s; public static Product newtypea(...){ return new Product(..., new TypeAStrategy()); public static Product newtypeb(...){ return new Product(..., new TypeBStrategy()); Dodatkowo wykorzystamy wzorzec Strategii (Strategy).. 10
Domain Model class InstallmentStrategy{ abstract void calculateinstallments(contract c); class TypeAStrategy{ void calculateinstallments(contract c){ Money inst = c.gettotal.allocate(3); Date d = c.getsigned(); c.addinstallment(inst[0], d); c.addinstallment(inst[1], d.addmonth(1)); c.addinstallment(inst[2], d.addmonth(2)); class TypeBStrategy{ void calculateinstallments(contract c){ Money inst = c.gettotal.allocate(2); Date d = c.getsigned(); c.addinstallment(inst[0], d.addmonth(6)); c.addinstallment(inst[1], d.addmonth(12)); Strategie umożliwiają łatwą rozbudowę aplikacji o nowe reguły biznesowe. 11
Domain Model class Product... public void calculateinstallments(contract c){ this.strategy.calculateinstallments(); class Contract... public void calculateinstallments(){ this.product.calculateinstallments(this) ; Przekazujemy zlecenie do obiektu, który jest najbardziej kompetentny do obsługi. Watro zwrócić uwagę na brak instrukcji warunkowych. Logika aplikacji jest wpisana w strukturę obiektów. 12
Domain Model Contract... getincome(date): Money calculateinstallments(): void * 1 Product calculateinstallments(contact): void * 1 InstallmentStrategy calculateinstallments(contract): void TypeAStrategy calculateinstallments(contract): void TypeBStrategy calculateinstallments(contract): void 13
Table Module Problemem Domain Model jest implementacja interfejsu relacyjnej bazy danych. We wzorcu Table Module logika dziedziny jest umieszczona w klasach odpowiadających poszczególnym tabelom w bazie danych. Podstawowa różnica względem Domain Model polega na tym, że obsługa wielu rekordów w bazie danych (egzemplarzy) jest realizowana przez jeden obiekt. 14
Table Module Contract... getincome(date) gettype(id) Product Installment insert(id, amount, date) 15
FilteredRowSet W Javie najodpowiedniejszym narzędziem umożliwiającym dostęp do danych we wzorcu TableModule jest interfejs javax.sql.rowset.filteredrowset: FilteredRowSet frs = new FilteredRowSetImpl(); frs.populate(rs); Range name = new Range("From", "To", "column"); frs.setfilter(name); frs.next() // to co zostanie zwrócone zależy od implementacji klasy Range() Filtr jest tworzony w oparciu o implementacje interfejsu javax.sql.rowset.predicate. 16
Predicate public class Range implements Predicate { private Object form, to; private String column; public Range(Object f, Object t, String s) { this.from = f; this.to = t; this.column = s; public boolean evaluate(rowset rs) { boolean b1, b2; if ((rs.getobject(this.column) >= this.from) && (rs.getobject(this.column) <= this.to)){ return true; // spelnia warunki filtra else { return false; 17
Table Module Klasa Contract zarządza wszystkimi operacjami wykonywanymi na wszystkich kontraktach. class Contract... private Rowset rows; public Contract(RowSet rs){ this.rows = rs; public ResultSet findcontract(int cid){ FilteredRowSet frs = new FilteredRowSetImpl(); frs.populate(rs); RSFilter filter = new RSFilter(cid, "con_id"); frs.setfilter(name); return frs; 18
Table Module Implementacja poszczególnych operacji jest bardzo podobna do tej ze wzorca TransactionScript. public void calculateinstallments(long cid){ try{ ResultSet rs = this.findcontract(cid); rs.next(); Date d = rs.getdate("signed"); Money total = rs.getbigdecimal("total"); String type = rs.getstring("type"); if (type.equals("a")){... catch(sqlexception ex){ throw new ApplicationException(ex); 19
Porównanie wzorców logiki dziedziny Transaction Script Table Module nakład pracy Domain Model złożoność 20
Service Layer Zwykle logikę biznesową dzieli się na dwa rodzaje: logikę dziedziny operacje wyłącznie na dziedzinie problemu (np. obliczanie rat), logikę aplikacji (pracy) funkcje aplikacji (logowanie, przesyłanie wiadomości o zmianach w dziedzinie itp.). W przypadku Domain Model obie funkcje są realizowane przez klasy obiektów dziedziny. Powoduje to brak możliwości użycia tych obiektów w innych aplikacjach (z inną logiką aplikacji) oraz problemy przy ewentualnych zmianach logiki aplikacji. Z tego powodu warto rozdzielić oba rodzaje logiki biznesowej i umieścić je w nowej warstwie warstwie usług. 21
Service Layer interfejsy użytkownika bramy integracyjne import danych warstwa usług model dziedziny warstwa źródła danych 22
Service Layer Sposoby implementacji: fasada dziedziny (domain facade) logika biznesowa w całości pozostaje w modelu dziedziny. Warstwa usług wyznacza zbiór operacji, które mogą być używane przez warstwy klienckie. skrypty operacji (operation scripts) zbiór klas bezpośrednio implementujących logikę aplikacji. Logika dziedziny jest realizowana przez hermetyczne klasy obiektów dziedziny. Zbiór klas skryptów operacji zwykle korzysta ze wzorca Layer Supertype okreslającego zakres funkcji oraz wspólne zachowania. 23
Layer supertype Wzorzec polega na wprowadzeniu klasy bazowej dla wszystkich klas danej warstwy. class DomainObject { private long id;... public long getid(){ return this.id; public void setid(long l){ this.id = l; 24
Service Layer a Java Najczęściej rolę warstwy Service Layer pełnią bezstanowe komponenty sesysjne wyposażone w interfejs lokalny. Podobnym wzorcem dla środowiska JEE jest wzorzec Session Facade, jednak jego podstawową funkcją jest poprawa wydajności poprzez osłonięcie komponentów encyjnych przed nadmierną ilością wywołań (EJB 2.0). Service Layer dzieli implementację aby zredukować powtórzenia kodu i umożliwić powtórne użycie obiektów dziedziny. 25
Podsumowanie Podstawowe wzorce logiki dziedziny to Transaction Script i Domain Model. Wzorzec Table Module można stosować, gdy język programowania w naturalny sposób dostarcza implementacji wzorca Record Set. Aby nie umieszczać logiki aplikacji w implementacji dziedziny wprowadzamy dodatkową warstwę - stosujemy wzorzec Service Layer. 26