SzoftverTechnikakTetelsor
Bináris komponensek evolúciója 1.
Soroljon fel három C++ tulajdonságot, amelyek alkalmatlanná teszik a nyelvet lazán csatolt komponensek fejlesztésére!
Elveszik a kompatibilitás ha az új lib verzióban:
- újraimplementálsz virtuális függvényeket
- hozzáadsz vagy elveszel virtuális tagfüggvényeket (asszem a virual fv. táblák méreteinek megváltozása átrendezi a kész lib szerkezetét)
- megváltoztatod az osztályhierarchiát (kivéve ha csak új leveleket adsz hozzá)
- elveszel/hozzáadsz privát adattagokat
- eltávolítasz public vagy protected nem inline metódust
- inlineként valósítasz meg régi (nem inline) public/protected metódusokat
- megváltoztatod egy inline metódus működését úgy, hogy a régi verzió így már nem működik megfelelően
szóval a dolog lényege szerintem az, hogy ha c++ shared libet próbálsz meg tovább fejleszteni, és nem tartod be ezeket a korlátozásokat, akkor az új libben máshová kerülnek azok a fv-ek is, amelyek a régi verzióban kerültek megvalósításra, így viszont az új verzióban nem fogják őket megtalálni a programok, magyarul nem leszel binárisan kompatibilis.
Hasonlítsa össze a statikus és a dinamikus programkönyvtárakat!
A dinamikus libek nem linkelési (fordítási) időben, vagy a program indulásakor töltődnek be, hanem egy API segítségével futásidőben. http://www.dwheeler.com/program-library/Program-Library-HOWTO/x170.html
Ismertesse a dinamikus könyvtárak elnevezésikonvenció-hierarchiáját!
->libakarmi.so: "linker name", szimlink a soname-re, ez alapján keresgél a linker
->libakarmi.so.3: "soname", általában szimlink a valódi névre, a vége a verzió szám (ami az interface változásakor nő)
->libakarmi.so.3.2.1: valódi név - soname + minor number + release number, ez már konkrétan a lib
Mutasson egy Linux VAGY Windows alatt futó példát dinamikus programkönyvtárak betöltésére, felszabadítására és egy könyvtári függvény meghívására! Hol jelenik meg a fordító szintjén lévő kapcsolódás?
Windows
windows.h-ban vannak a szükséges függvények, ezért:
#include "windows.h"
Betöltetjük az OS-el a dll-elünket, amire egy HMODULE típusú változóval tudunk a későbbiekben hivatkozni
HMODULE hDLL = !LoadLibrary("myDll.dll");
Persze lehet, hogy nincs is dll, ekkor null-t kapunk vissza.
if (hDLL == NULL) { fprintf(stderr, "Cannot find DLL\n"); //kiírjuk az stderr-re, hogy baj van return -1; //végül kilépünk }
Van már DLL-elünk, valahogy kellene használni a benne lévő függvényeket is. Megkérjük a Windows-t, hogy a DLL-ből keresse ki a megadott nevű függvényt, és ha szerencsénk van, vissza kapunk egy rá mutató függvény pointert. Az élet nem ennyire egyszerű, mert elképzelhető, hogy van a függvényünknek visszatérési értéke, akár még paramétere is lehet. A fordítónknak sem ártana tudnia, hogy hány darab és milyen méretű paramétert kell le push-nia a stack-be. Legyen pl. egy olyan függvény, ami 2 szám közül visszaadja a nagyobbat. Rakjuk el egy változóba, de már meg kell adni a visszatérési érték és a paraméterek típusát is.
int (*fvMax)(int, int);
Ezt a csúnya és hosszú sort még minimum 2x le kellene írni, ezért typedefelünk:
typedef int(*MY_MAX_FUNC)(int, int); ... MY_MAX_FUNC fvMax;
Van egy függvény pointer típusunk, amely int-et ad vissza, és 2 db int a paramétere. Nem adtuk meg, hogy mik a paraméterek nevei. Erre a fordító magasról sz*rik, őt csak a típusok érdeklik. Töltsük be.
fvMax = (MY_MAX_FUNC)GetProcAddress(hDLL, "add");
A GetProcAddress egy void*-ot ad vissza, azaz egy általános pointert, ami a függvényre mutat a memóriában. Csakhogy nekünk spéci függvényünk van, ezért cast-olunk. Itt már kezdünk örülni a typedef-nek, hogy nem kell mindenféle csúnya zárójeles csillagos dolgokat leírni.
if (fvMax == NULL) { fprintf(stderr, %MAROON%"Cannot find function\n"); return -1; }
Ha nincs szerencsénk, NULL-t kapunk vissza.
Ennyi küzdelem után jó lenne használni is:
int max = (*MY_MAX_FUNC)(a, b); // Valoszinuleg inkabb int max = (*fvMax)(a, b);
A MY_MAX_FUNC egy függvény pointer, de mi nem a címet szeretnénk, hanem megakarjuk hívni. A * pont ezért kell, hogy a pointerre rámutassunk. C-ben zárójelekkel () jelezzük, hogy a változónk egy függvény, és hogy hívni szeretnénk, ezért olyan a typedef. Tehát elmutatunk a memóriában valahova, amiről tudjuk, hogy egy függvény, és meghívjuk a, b int-ekkel, és a visszatérési értéket elrakjuk max-ba.
Végül illene közölni a Windows-al, hogy már nem szeretnénk használni a dll-t, akár ki is szedheti a memóriából.
FreeLibrary(hDLL);
Linux
Linux-on is kb ugyanezt csináljuk, csak másképp.
#include <dlfcn.h> #include <stdio.h> typedef int(*MY_MAX_FUNC)(int, int); int main(){ MY_MAX_FUNC fvMax; void *handle; /* Megnyitjuk a konyvtarat. */ handle = dlopen("./libcomplex.so", RTLD_LAZY); if(!handle){ fputs(dlerror(), stderr); return 1; } /* Hozzaferunk a szimbolumhoz. */ fvMax = dlsym(handle, "max"); if(dlerror() != NULL){ fputs(dlerror(), stderr); return 1; } int max = (*fvMax)(a, b); // int max = fvMax(a, b); is ugyanugy mukodik dlclose(handle); return 0; }
Bináris komponensek evolúciója 2.
Hogyan lehet egy osztály metódusait/tagváltozóit lekérdezni? Mutasson egy C# VAGY egy Java példát!
Java:
import java.lang.reflect.*; import java.awt.*; class SampleField { public static void main(String[] args) { GridBagConstraints g = new GridBagConstraints(); printFieldNames(g); } static void printFieldNamesAndMethods(Object o) { Class c = o.getClass(); Field[] publicFields = c.getFields(); Method[] theMethods = c.getMethods(); // Tagvaltozok lekerdezese for (int i = 0; i < publicFields.length; i++) { String fieldName = publicFields[i].getName(); Class typeClass = publicFields[i].getType(); String fieldType = typeClass.getName(); System.out.println("Name: " + fieldName + ", Type: " + fieldType); } } // Metodusok lekerdezese for (int i = 0; i < theMethods.length; i++) { String methodString = theMethods[i].getName(); System.out.println("Name: " + methodString); String returnString = theMethods[i].getReturnType().getName(); System.out.println(" Return Type: " + returnString); Class[] parameterTypes = theMethods[i].getParameterTypes(); System.out.print(" Parameter Types:"); for (int k = 0; k < parameterTypes.length; k ++) { String parameterString = parameterTypes[k].getName(); System.out.print(" " + parameterString); } System.out.println(); } } }
Ahogy ezt megszokhattuk, a fuggvenynevek eleg beszedesek...
Hasonlítsa össze a C/C++ nyelv; bináris komponenseket a modern futtatókörnyezetek megoldásaival! Miért van szükség reflexióra?
sun.com szerint azert van szukseg reflexiora, mert igy lehet pl. debuggereket, class browsereket es GUI buildereket irni.
Viewing metadata This might be used by tools and utilities that wish to display metadata. Performing type discovery This allows you to examine the types in an assembly and interact with or instantiate those types. This can be useful in creating custom scripts. For example, you might want to allow your users to interact with your program using a script language, such as JavaScript, or a scripting language you create yourself. Late binding to methods and properties This allows the programmer to invoke properties and methods on objects dynamically instantiated based on type discovery. This is also known as dynamic invocation. Creating types at runtime (Reflection Emit) The ultimate use of reflection is to create new types at runtime and then to use those types to perform tasks. You might do this when a custom class, created at runtime, will run significantly faster than more generic code created at compile time.
Mutasson példát attribútumokra C# nyelven VAGY annotációkra Java nyelven (saját létrehozása, használat, lekérdezés)!
Létrehozás, ez ugye innentől .NET és attribútum:
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)] public class TableAttribute : System.Attribute { private string _name; public string Name { get { return _name; } set { _name = value; } } public TableAttribute(string name) { _name = name; } }
Használat:
[Table("Customer")] public class Customer { // Anything... } // Lekerdezes: private string GetTable(Type type) { object[] tables = type. GetCustomAttributes(typeof(TableAttribute), true); if (tables.Length == 1) return (tables[0] as TableAttribute).Name; else return null; }
A lenyeg (szerintem): az attributumok a System.Attribute-ból származnak, a nevük végén ott az "Attribute" (??), szögletes zárójelben lehet használni őket (az Attribute végződés nélkül), lekérdezés mega a Type.GetCustomAttributes()-szal megy.
Adatkezelés 1.
Mutasson egy példát a három anomáliatípusra! Küszöbölje ki a példa anomáliáit BCNF dekompozícióval!
- törlési anomália
Egy szükségtelen adat törlése magával ránt hasznos információt is.
- beszúrási anomália
Inkonzisztens adatok szúrhatók be a táblába, pl.:
Név | Osztály száma | Osztály neve |
Ede | 42 | Takarítók |
Gizi | 42 | IT |
Akkor Gizi most takarít, vagy bitet heggeszt?
- módosítási anomália
Redundánsan tárolt adat megváltoztatásához az összes tárolási ponton változtatni kell
Mutassa be az objektum-relációs leképezést! Adjon meg példaként osztálydiagramot, amely tartalmaz 1-1, 1-több, több-több kapcsolatot! Képezze le ezeket adatbázistáblákba!
Adatkezelés 2.
Ismertesse egy rövid C# nyelv; példán keresztül az ADO.NET kapcsolatalapú adathozzáférését!
SqlConnection conn = null; try { // Kapcsolódás azadatbázishoz conn = new SqlConnection(@"Data Source=LAPTOP\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True"); // A kapcsolat megnyitása conn.Open(); // Az adatbázis parancs létrehozása SqlCommand command = new SqlCommand("SELECT ShipperID, CompanyName, Phone FROM Shippers"); // Adatbázis kapcsolat megadása command.Connection = conn; Console.WriteLine("{0,0}{1,15}{2,15}", "ShipperID", "CompanyName", "Phone"); Console.WriteLine("-----------------------------------------------------------------"); // Az adatok lekérdezése és kiiratása using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) Console.WriteLine("{0,4}{1,20}{2,20}", reader["ShipperID"].ToString(), reader["CompanyName"].ToString(), reader["Phone"].ToString()); } } catch (Exception ex) { // Kivétel szövegének kiiratása Console.WriteLine(ex.Message); } finally { // Az adatbázis kapcsolat lezárása, ha meg lett nyitva if((conn!=null)&&(conn.State==System.Data.ConnectionState.Open)) conn.Close(); }
Ismertesse egy rövid C# nyelv; példán keresztül az ADO.NET kapcsolat nélküli adathozzáférését!
using System; using System.Data; using System.Data.SqlClient; using System.Drawing; using System.Windows.forms; class DisconnectedDataform : form { private SqlConnection conn; private SqlDataAdapter daCustomers; private DataSet dsCustomers; private DataGrid dgCustomers; private const string tableName = "Customers"; // initialize form with DataGrid and Button public DisconnectedDataform() { // fill dataset Initdata(); // set up datagrid dgCustomers = new DataGrid(); dgCustomers.Location = new Point(5, 5); dgCustomers.Size = new Size( this.Clientrectangle.Size.Width - 10, this.Clientrectangle.Height - 50); dgCustomers.DataSource = dsCustomers; dgCustomers.DataMember = tableName; // create update button Button btnUpdate = new Button(); btnUpdate.Text = "Update"; btnUpdate.Location = new Point( this.Clientrectangle.Width/2 - btnUpdate.Width/2, this.Clientrectangle.Height - (btnUpdate.Height + 10)); btnUpdate.Click += new EventHandler(btnUpdateClicked); // make sure controls appear on form Controls.AddRange(new Control[] { dgCustomers, btnUpdate }); } // set up ADO.NET objects public void Initdata() { // instantiate the connection conn = new SqlConnection( "Server=(local);DataBase=Northwind;Integrated Security=SSPI"); // 1. instantiate a new DataSet dsCustomers = new DataSet(); // 2. init SqlDataAdapter with select command and connection daCustomers = new SqlDataAdapter( "select CustomerID, CompanyName from Customers", conn); // 3. fill in insert, update, and delete commands SqlCommandBuilder cmdBldr = new SqlCommandBuilder(daCustomers); // 4. fill the dataset daCustomers.Fill(dsCustomers, tableName); } // Update button was clicked public void btnUpdateClicked(object sender, EventArgs e) { // write changes back to DataBase daCustomers.Update(dsCustomers, tableName); } // start the Windows form static void Main() { Application.Run(new DisconnectedDataform()); } }
using System; using System.Data; using System.Data.SqlClient; class SelectIntoDataSet{ public static void Main(){ string connectionString = "server=(local)\\SQLEXPRESS;database=MyDatabase;Integrated Security=SSPI"; SqlConnection mySqlConnection = new SqlConnection(connectionString); string selectString = "SELECT TOP 10 ID, FirstName, LastName FROM Employee ORDER BY ID"; SqlCommand mySqlCommand = mySqlConnection.CreateCommand(); mySqlCommand.CommandText = selectString; SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(); mySqlDataAdapter.SelectCommand = mySqlCommand; DataSet myDataSet = new DataSet(); mySqlConnection.Open(); Console.WriteLine("Retrieving rows from the Employee table"); mySqlDataAdapter.Fill(myDataSet, "Employee"); mySqlConnection.Close(); DataTable myDataTable = myDataSet.Tables["Employee"]; foreach (DataRow myDataRow in myDataTable.Rows){ Console.WriteLine("ID = "+ myDataRow["ID"]); Console.WriteLine("FirstName = "+ myDataRow["FirstName"]); Console.WriteLine("LastName = "+ myDataRow["LastName"]); } } }
Ismertesse az adatkötés fogalmát!
Def1: Az adatkötés az adatforrásokból származó adatok összerendelése az adatmegjelenítő vezérlőkkel. Def2: Adatkötés alatt azt a folyamatot értjük, mely segítségével kiolvassuk a szükséges adatokat az adatforrásból és dinamikusan egy vizuális elem egyik tulajdonságához "kötjük", azaz a vizuális elem szóban forgó tulajdonságának értékét az adatforrás bizonyos adataitól tesszük (kölcsönösen) függővé.
Mutasson egy példát egy osztály perzisztenssé tételére a Java Persistence API segítségével! Ismertesse a szükséges annotációk jelentését! Mire használjuk a @Transient annotációt?
@Entity public class Foo { // perzisztens osztály @Id protected int id; // elsődleges kulcs protected int x; protected int y; @Transient protected int z; // nem lesz elmentve az adatbázisba }