ČESKÁ ZEMĚDĚLSKÁ UNIVERZITA FAKULTA PROVOZNĚ EKONOMICKÁ Obor: Informatika - magisterský DIPLOMOVÁ PRÁCE Téma: Aplikační frameworky v objektově orientovaných jazycích Vypracoval: Vedoucí diplomové práce: Bc. Jan Tauchmann doc. Ing. Vojtěch Merunka Ph.D. Praha 2009
Aplikační frameworky v objektově orientovaných jazycích The application frameworks for the object oriented languages
Kapitola 1: Stránka 1
Kapitola 2: Stránka 2
Kapitola 3: Stránka 3
Kapitola 3: Stránka 4
LD A,2 LD B,3 CALL SOUCET # zavolání podprogramu SOUCET # registry A a B zde nahrazují parametry :SOUCET ADD A,B RET Kapitola 4: Stránka 5
Definice knihovny "matice": Unit Matice; Interface Type TMatice = Array [1..5, 1..5] of Integer; function Diskriminant(var mat: TMatice): Integer; Implementation function Diskriminant(var mat: TMatice): Integer; begin... zde je implementace vypočtu diskriminantu } end; begin End. Využití knihovny matice v jiném programu: Program MaticeGui; Uses Crt, Matice; var mat: TMatice; Begin... zde se nactou hodnoty matice do promenne "mat" z klavesnice} WriteLn('Vysledek je:'); WriteLn(Diskriminant(mat)); End. Kapitola 4: Stránka 6
Program Form1; var myform : TForm; procedure b_okclick(sender:tobject); begin myform.close; end; begin myform := TForm.Create(Application) ; //vytvoření formuláře //vytvoření tlačítka na formuláři TButton okbutton := TButton.Create(myForm); okbutton.left := 22; okbutton.right := 47; okbutton.label := "OK"; //přiřazení procedury b_okclick události kliknutí na tlačítko okbutton okbutton.onclick := b_okclick; myform.show; //zobrazení formuláře end. Kapitola 4: Stránka 7
Aplikace Komponenta A Komponenta B "Nested komponenta" X1 "Nested komponenta" X2 Výchozí API vývojového prostředí (framework) API operačního systému Kapitola 4: Stránka 8
Kapitola 4: Stránka 9
protected void Button1_OnClick(object sender, EventArgs e) connection.execsql("update pohledavky set value='"+txtvalue.text+"'"); } Kapitola 5: Stránka 10
Kapitola 5: Stránka 11
Model c) Prezentační vrstva se dotáže na zaktualizovaný model View a) Controller upraví objekty doménové logiky na základě uživatelského vstupu b) Controller požádá prezentační vrstvu o zobrazení aktuálního stavu Controller Kapitola 5: Stránka 12
Evidence klientů E-shop Autentizace Správa Objednávky Produktový katalog Intranet Zobrazení/správa Catalog Sklad Obsah/Rezervace Správa skladu Kapitola 5: Stránka 13
SerializationContext 0..* 0..* 0..1 0..1 + + ISerializable Serialize (ObjectStream stream) Deserialize (ObjectStream stream) : void : void 0..* 0..1 ObjectSerializer - - + + Items : Item[] Status : int <<Implement>> <<Implement>> ShoppingCart Serialize (ObjectStream stream) Deserialize (ObjectStream stream) : void : void - - + + Name Price : string : float <<Implement>> <<Implement>> Product Serialize (ObjectStream stream) Deserialize (ObjectStream stream) : void : void Kapitola 5: Stránka 14
Kapitola 5: Stránka 15
Kapitola 5: Stránka 16
Kapitola 6: Stránka 17
Interní firemní framework Vlastní implementace IoC kontejneru Vlastní implemetace layout manageru Ukázky použití + best practices Log4Net (logování) Infragistics (uživ. rozhraní) NHibernate (ORM) ODP.NET (přístup k DB) Kapitola 6: Stránka 18
Kapitola 6: Stránka 19
Kapitola 7: Stránka 20
IObjectFactory factory = ContextRegistry.GetContext(); Connection connection = (Connection)factory.GetObject("myDbConnection"); <object id="mydbconnection" type="sytem.data.oracledbconnection"> <property name="connectionstring" value="datasource=serv1;user id=scott;password=tiger"/> <property name="connectiontimeout" value="120" /> <property name="keepalive" value="false" /> </object> public Connection GetMyDbConnection() Type t = Type.GetType(Configuration["Type"]); Connection conn = (Connection)Activator.CreateInstance(t); conn.connectionstring = (string)configuration["connectionstring"]; conn.connectiontimeout = (int)configuration["connectiontimeout"]; conn.keepalive = (bool)configuration["keepalive"]; return conn; } Kapitola 7: Stránka 21
Uživatelské rozhraní void SaveUser(User user) User GetUser(int id) Business logika void ExecSQL(...) DataReader GetSQLResult(...) Přístup k databázi (DAO) public void btnsave_click() //uložíme informace o uživateli currentuser UserLogic.SaveUser(currentUser); } Kapitola 7: Stránka 22
void SaveUser(User user) Interface void ExecSQL(...) Interface Uživatelské rozhraní Business logika Přístup k databázi (DAO) GetObject("UserLogic") Objekt UserLogic GetObject("DAO") Objekt DAO Spring framework public void btnsave_click() //uložíme informace o uživateli currentuser IObjectFactory factory = ContextRegistry.GetContext(); IUserLogic userlogic = (IUserLogic) factory.getobject("userlogic"); userlogic.saveuser(currentuser); } <objects xmlns="http://www.springframework.net"> <object id="userlogic" type="shopping.bl.userlogic"> <property name="minusernamelength" value="4" /> </object> </objects> public class UserLogic: IUserLogic private int minusernamelength; public int MinUserNameLength get return minusernamelength; } set minusernamelength = value; } } Kapitola 7: Stránka 23
public void Save(User user) //validace if (user.name.length < MinUserNameLength) throw new ValidationException("User name's length must be greater then " + MinUserNameLength); } } //implementace uložení objektu user //.. //.. Uživatelské rozhraní Neexistuje žádný použitelný interface Business logika Neexistuje žádný použitelný interface Přístup k databázi (DAO) začne vyvvíjet za 4 měsíce začně vyvíjet za 2 měsíce vyvíjí Čeká až bude mít DAO vývojář první funkční verzi Čeká až bude mít DAO vývojář první funkční verzi 1) 2) public class UserLogic: IUserLogic public User GetUser(long id) Kapitola 7: Stránka 24
} } //Volání DAO vrstvy return DAO.GetObject<User>(id, Record.Current); public class UserLogicFake: IUserLogic public User GetUser(long id) User user = new User(); user.id = id; user.name = "Jan Novák"; //... ruční vyplnění dalších atributů //... return user; } } [TestFixture] public class UserLogicUnitTest public void SaveUserTest() User user = new User(); user.id = id; user.name = "Jan Novák"; //...vyplnění dalších povinných položek userlogic.saveuser(user); Assert.AreEqual(user.LastModified,DateTime.Now); //...další Assert příkazy } } <objects xmlns="http://www.springframework.net"> <object id="testuser1" type="shopping.be.user"> <property name="name" value="jan Novák" /> <property name="birthdate" value="1.2.1981" /> <!--...vyplnění všech atributů --> </object> <object id="testuser2" type="shopping.be.user"> <property name="name" value="jana Nováková" /> <!--...vyplnění všech atributů --> </object> </objects> public void SaveUserTest() Kapitola 7: Stránka 25
} IObjectFactory factory = ContextRegistry.GetContext(); User user = (IUserLogic) factory.getobject("testuser2"); userlogic.saveuser(user); //... Assert příkazy Kapitola 7: Stránka 26
Webový server WWW prohlížeč (Firefox, IE...) HTTP požadavek HTTP odpověd HTTP server index.php picture.jpg style.css 1) 2) o o Kapitola 8: Stránka 27
<html> <body> Dnes je: <? print date()?> </body> </html> <html> <body> Dnes je: 23.12.2007 13:21 </body> </html> Kapitola 8: Stránka 28
XML konfigurace Http požadavek Webový kontejner Volá Servlet 1. Volání Business logika (získání dat, výpočty) 2. Business objekty 3. Přesměrování na JSP Http odpověď JSP stránka Přístup pomocí URL /register RegisterServlet Nový požadavek nebo Validační chyby Úspěšná registrace register.jsp registrationsuccess.jsp <body> <font color="red">$validationmessage}</font> <form action="register"> Name: <input name="name" /><br /> Password: <input name="password" /><br /> <!-- další položky formuláře --> <input type="submit" /> </form> </body> public class RegisterServlet extends HttpServlet public void doget (HttpServletRequest req,httpservletresponse res) Kapitola 8: Stránka 29
String name = req.parameters["name"]; String password = req.parameters["password"]; length"; formulář //validace vstupu if(password==null password.length<3) req.attributes["validationmessage"] = "Invalid password's } req.forward("register.jsp"); //přesměrování zpět na return; //volání business logiky pro založení uživatele UserLogic logic=new UserLogic(); User user=logic.registernewuser(name, password,...); if(user!=null) //zpřístupnění objektu "user" v JSP stránce req.attributes["user"] = user; req.forward("registrationsuccess.jsp"); //přesměrování } else //nastavení validační hlášky req.attributes["validationmessage"] = "Your name is already used."; req.forward("register.jsp"); //přesměrování zpět na formulář } } <body> Registration process for user registered with ID: $user.id} </body> $user.name} succeeded. User Kapitola 8: Stránka 30
o o o o Webový formulář Odeslání formuláře Http požadavek:?user=root&pass=12345 Přijetí požadavku ActionServletem a vyplnění LoginFormBean LoginFormBean User=root Pass=12345 Kapitola 8: Stránka 31
Konfigurace (struts-config.xml) Form bean Vytváří Využívá Http požadavek /nejaka_cesta Volá Web kontejner Action servlet Action třída Volá Business logika Přesměruje volání na Http odpověď JSP stránka Využívá Kapitola 8: Stránka 32
o o <struts-config> <!-- definice form beanu RegisterForm --> <form-beans> <form-bean name="registerform" type="myapp.registerform" /> </form-beans> <!-- validace uživatelského vstupu --> <form-validation> <formset> <form name="registerform"> <field property="username" depends="required,minlength"> <arg0 key="user.name"/> <arg1 name="minlength" key="3"/> </field> </form> </formset> </form-validation> <!-- namapování URL /register na akci RegisterAction --> <action-mappings> <action path="/register" type="myapp.registeraction" name="registerform" scope="request"> <!-- definice JSP souboru pro stavy "success" a "failure" --> <forward name="failure" path="/register.jsp"/> <forward name="success" path="/registrationsuccess.jsp"/> </action> </action-mappings> </struts-config> public class RegisterAction extends Action public ActionForward execute(actionmapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) Kapitola 8: Stránka 33
...); } throws Exception //volání business logiky pro založení uživatele UserLogic logic=new UserLogic(); RegisterForm rf=(registerform)form; User user=logic.registernewuser(rf.getname(), rf.getpass(), } if(user!=null) //zpřístupnění objektu "user" v JSP stránce req.attributes["user"] = user; //dokončení běhu se stavem "success" return (mapping.findforward("success")); } else //dokončení běhu se stavem "failure" return (mapping.findforward("failure")); } Kapitola 8: Stránka 34
Kapitola 8: Stránka 35
<body> <form id="form1" runat="server"> <asp:button ID="btnSubmit" runat="server" text="odeslat" /> </form> </body> protected void Page_Load(object sender, EventArgs e) //vytvoříme instanci tlačítka Button mybutton=new Button(); //vyplníme potřebné vlastnosti mybutton.text = "Odeslat"; mybutton.id = "btnsubmit"; //přidáme tlačítko na formulář Form.Controls.Add(myButton); } Kapitola 8: Stránka 36
Klik na tlačítko HTML stránka Http požadavek Http odpověď Strom komponent (umístěn v paměti po dobu zpracování požadavku) FacesServlet / PageHandlerFactory Automatické události Zavolání události komponenty tlačítko Soubor (ASPX / JSP) Kód obsluhy událostí stránky (načtení dat / renderování) Kód obsluhy události "Klik na tlačítko Login" Business logika Kapitola 8: Stránka 37
Kapitola 8: Stránka 38
User GetUser() SELECT... Business vrstva void SaveUser(User) DAL INSERT... Databáze public static List<User> GetUserById(long id) List<User> result=new List<User>(); try // Vytvoříme instanci DbCommand. DbCommand command = connection.createcommand(); Kapitola 9: Stránka 39
} command.commandtext = "SELECT * FROM USERS WHERE ID=@id"; command.parameters["@id"] = id; //spustíme příkaz using (DbDataReader reader = command.executereader()) //vyplníme kolekci výsledku vrácenými daty while (reader.read()) User user = new User(); user.name = reader["name"] as string; user.address = reader["address"] as string; //... mapování dalších položek result.add(user); } } return result; } catch (Exception e) //zpracování vyjímek } o o Kapitola 9: Stránka 40
Konfigurace SaveObject(Object obj) GetObject(Type type,string wherestring) ORM INSERT... SELECT... Databáze User GetUser() GetObject(..) SELECT... Business vrstva void SaveUser(User) DAL SaveObject(...) ORM INSERT... Databáze public static List<User> GetUserById(long id) try return ormsession.getobject(typeof(user), "ID=" + id); } catch (Exception e) //zpracování vyjímek } } Kapitola 9: Stránka 41
<mapping> <class name="user" dbname="user"> <attribute name="name" dbname="fullname" /> <attribute name="salary" dbname="annual_salary" /> </class> </mapping> Kapitola 9: Stránka 42
Konfigurace GetObject(...) SELECT... Business vrstva SaveObject(...) Hibernate INSERT... Databáze Objekty business logiky XML mapovací soubor Databázové schéma Kapitola 9: Stránka 43
o o Kapitola 9: Stránka 44
ArrayList numbers = new ArrayList() 1, 2, 3, 4, 5 }; ArrayList result = new ArrayList(); foreach (int i in numbers) if(i<4) result.add(i); } result.sort(delegate(int a, int b) return b.compareto(a); }); ArrayList numbers = new ArrayList() 1, 2, 3, 4, 5 }; var result = numbers.where(n => n < 4).OrderByDescending(n => n); numbers Where(), n => n < 4. OrderByDescending() ArrayList numbers = new ArrayList() 1, 2, 3, 4, 5 }; var result = from int n in numbers where n < 4 orderby n descending select n; Kapitola 9: Stránka 45
Kapitola 9: Stránka 46
Testovaný kód Kód unit testu Testovací framework Kapitola 10: Stránka 47
public class StringUtil public string Concat(string s1, string s2) //pro názornost uděláme úmyslnou chybu return s1 + s1; } } [TestFixture] public class StringUtilTest [Test] public void ConcatTest() StringUtil util = new StringUtil(); string result = util.concat("a", "B"); Assert.AreEqual("AB", result, "Concatenation"); } } Kapitola 10: Stránka 48
Kapitola 10: Stránka 49
Unit tests U I A P P L I C A T I O N B U S I N E S S D A T A A C C E S S DATA Kapitola 10: Stránka 50
Console.WriteLine("Starting UI test scenario"); Console.WriteLine("Launching WinApp under test"); string exepath = @"..\\..\WinApp\bin\Debug\WinApp.exe"; testform = LaunchApp(exePath, "WinApp.Form1"); //spuštění aplikace Console.WriteLine("Setting textbox1 to 'yellow'"); SetControlPropertyValue("textBox1", "Text", "yellow"); Console.WriteLine("Setting textbox1 to 'red'"); SetControlPropertyValue("textBox1", "Text", "red"); Console.WriteLine("Selecting combobox1 to 'blue'"); SetControlPropertyValue("comboBox1", "SelectedItem", "blue"); Console.WriteLine("Clicking button1"); InvokeMethod("button1_Click", new object[]null, new EventArgs()} ); Console.WriteLine("Checking for state: 'red', 'blue', 'Result is purple'"); bool pass = true; if (!((ListBox.ObjectCollection)GetControlPropertyValue("listBox1", "Items")).Contains("Result is purple")) pass = false; if (pass) Console.WriteLine("\nUI test scenario result: PASS"); else Console.WriteLine("\nUI test scenario result: *FAIL*"); Kapitola 10: Stránka 51
WebConversation wc = new WebConversation(); //načtení stránky localhost/index.aspx WebResponse resp = wc.getresponse("http://localhost/search.aspx" ); //použití DOM k získání formuláře na stránce WebForm form = resp.getforms()[0] //nastavení parametru formuláře Name na hodnotu Novák Jiří form.setparameter( "Name", "Novák Jiří") form.submit(); //odeslání formuláře //načtení stránky, na kterou se odkazoval link Kapitola 10: Stránka 52
WebResponse resp = wc.getcurrentpage(); //použití DOM k získání první tabulky na stránce WebTable table = resp.gettables()[0]; //kontrola počtu řádků v tabulce Assert.AreEqual( "rows", 4, table.getrowcount() ); //kontrola počtu sloupců v tabulce Assert.AreEqual( "columns", 3, table.getcolumncount() ); //kontrola počtu hypertextových odkazů v buňce s indexem (0,2) Assert.AreEqual( "links", 1, table.gettablecell( 0, 2 ).getlinks().length ); Kapitola 10: Stránka 53
Kapitola 10: Stránka 54
Kapitola 10: Stránka 55
Kapitola 10: Stránka 56
Kapitola 11: Stránka 57
public class Calculator ILog log = LogManager.GetLogger("Calculator"); public void Compute() try //... // zápis zprávy se závažností info log.info("příprava výpočtu kapitálové přiměřenosti"); //... //zápis zpravy se závažností warn log.warn("vstupní soubor je možná poškozen."); //... } catch (Exception e) Kapitola 11: Stránka 58
} } //zápis informace o selhání log.fatal("výpočet kapitálové přiměřenosti selhal",e); //... <log4net> <root> <level value="warn" /> <appender-ref ref="logfileappender" /> <appender-ref ref="consoleappender" /> </root> <appender name="logfileappender" type="log4net.appender.fileappender" > <param name="file" value="log-file.txt" /> <param name="appendtofile" value="true" /> <layout type="log4net.layout.patternlayout"> <param name="header" value="log soubor aplikace Calculator\r\n"/> <param name="footer" value="-----\r\nkonec"/> <param name="conversionpattern" value="%d [%t] %-5p %logger - %m%newline" /> </layout> <filter type="log4net.filter.levelrangefilter"> <param name="levelmin" value="info" /> <param name="levelmax" value="fatal" /> </filter> </appender> <appender name="consoleappender" type="log4net.appender.consoleappender" > <layout type="log4net.layout.patternlayout"> <param name="conversionpattern" value="%d [%t] %-5p %c[%x] %Xauth} %m%n" /> </layout> </appender> </log4net> o Kapitola 11: Stránka 59
o o Kapitola 11: Stránka 60
Kapitola 12: Stránka 61
<project name="ukazka_1" default="init"> <target name="init"> <mkdir dir="bin"/> <chmod file="bin" perm="a+xr"/> </target> </project> <project name="ukazka_2" default="compile"> <target name="compile"> <mkdir dir="bin"/> <chmod file="bin" perm="a+xr"/> <javac srcdir="src" destdir="bin"/> </target> <target name="clean"> <delete dir="bin"/> </target> Kapitola 12: Stránka 62
</project> Soubor example.properties bindir=c:\example\release\bin Soubor build.xml <project name="ukazka_3" default="init" basedir="."> <property file="example.properies" prefix="ext"/> <target name="compile"> <mkdir dir="$ext.bindir}"/> <chmod file="$ext.bindir}" perm="a+xr"/> <javac srcdir="src" destdir="$ext.bindir}"/> </target> </project> Kapitola 12: Stránka 63
Kapitola 13: Stránka 64
Kapitola 14: Stránka 65
Kapitola 14: Stránka 66
Kapitola 14: Stránka 67
Kapitola 15: Stránka 68
Kapitola 15: Stránka 69
Kapitola 16: Stránka 70
Kapitola 16: Stránka 71