SzoftverTechnikakTetelsor

A VIK Wikiből

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.

--forrás

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());
	}
 }

--forrás

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"]);
		}
	}
}

--forrás

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
}