A következő címkéjű bejegyzések mutatása: Vágjuk ki a magas Cét. Összes bejegyzés megjelenítése
A következő címkéjű bejegyzések mutatása: Vágjuk ki a magas Cét. Összes bejegyzés megjelenítése

2012. április 7., szombat

Egy kiváló magyar Silverlight blog


Azoknak akik még kimerik ejteni a szájukon a Silverlight szót, és netalántán még mindig érdeklődnek a technológia iránt, azok számára ajánlom Csala Péter blogját, ahol rengeteg hasznos anyag található meg a Silverlightal kapcsolatosan. Az egyik kedvenc anyagom a blogról:
  1. Egy komplexebb Silverlight-os alkalmazás lefejlesztése videó sorozat – I/IV. rész
  2. Egy komplexebb Silverlight-os alkalmazás lefejlesztése videó sorozat – II/IV. rész
  3. Egy komplexebb Silverlight-os alkalmazás lefejlesztése videó sorozat – III/IV. rész
  4. Egy komplexebb Silverlight-os alkalmazás lefejlesztése videó sorozat – IV/IV. rész
  5. Egy komplexebb Silverlight-os alkalmazás lefejlesztése videó sorozat – V/IV. rész
És a könyv, amit az első 24 órában több mint 4000-en töltöttek le:


Sajnos a blog jelenleg leállt, a Silverlight 5-rő semmi sincs fent, kár érte. Persze ma már tudjuk, hogy a Silverlight jövője nem fényes, de attól még mindig egy igen produktív eszköz a Mac OS X támogatás miatt! Ez nem elhanyagolható előnye még mindig a technológiának. Szóval aki WPF-ben jártas, ne hagyja ki csak azért mert nem ez a jövő...

2012. január 12., csütörtök

Elérhető a Bing Maps WPF Kontrol v1.0

 

A mai napon végre megjelent az általam már régóta várt Bing Maps kontroll WPF-re szánt verziója. A Bing Maps funkcióit a Silverlight fejlesztők már régóta élvezik, de a WPF projektekhez ezidáig csak fizetős megoldások voltak elérhetőek.  Szerencsére ennek mától vége, mivel az MS odaláról letölthető a WPF-re szánt hivatalos vezérlő...

2012. január 9., hétfő

Ingyenes Windows Phone szakkönyv

A devPortal.hu gondozásában megjelent és elektronikus formában ingyenesen letölthető a "Windows Phone fejlesztés lépésről lépésre" című szakkönyv. A magyarországi Windows Phone szakmai közösség aktív munkájával létrejött könyvet olyan szakemberek írták, akik korábban már megmutatták, hogy értenek a platform használatához és programozásához.

2011. december 10., szombat

Silverlight 5 – Egy hosszú út vége?

Végre megérkezett a Silverlight legújabb kiadása. A kérdés csak az, hogy tudunk e örülni neki? A korábbi pletykák szerint a Silverlight 5 kizárólag Windows és Internet Explorer környezetben lesz használható. A beépülő gyakorlatilag így megszűnne keresztplatformos megoldás lenni. Szerencsére ma már tudjuk, hogy ez nem igaz! Természetesen a Silverlight továbbra is működik minden jelentős browser-en, IE7-től felfelé, Firefox 3.6-től felfelé, Chrome 12-től felfelé – illetve Macintosh-on Firefox és Safari alatt. A P-Invoke és a DirectX-re támaszkodó 3D azonban csak Windows-on érhető el. A Silverlight 5 erősebb, mint valaha, a támogatással kapcsolatosan mindent elmond a lenti ábra valamint a Microsoft által ígért 10 év support ciklus! Valaki tud ezzel versenyezni ma?

2011. október 28., péntek

2011. október 2., vasárnap

WPF Alapok - Bevezetés

Egy ideje már tanulom a WPF-et  (Windows Presentation Foundation) és úgy gondoltam, hogy a kezdeti tapasztalataimat megosztom másokkal is. Mindezt azért teszem, mert ez a technológia teljesen lenyűgözött és szeretnék kis segítséget adni a Windows Forms-ról WPF-re való áttéréshez. Mert ez sajnos a legtöbbünknek nem fájdalommentes és főleg nem küzdelemmentes folyamat.

2011. augusztus 3., szerda

.NET Gadgeteer - nyílt forrású "kütyüplatform" a Microsofttól

[prog.hu] A Microsoft kutatórészlege a hét elején jelentett be egy új a .NET beágyazott eszközökbe szánt kiadásán, a Micro Framework-ön alapuló platformot, amelyet kifejezetten otthoni kütyük építésére hoztak létre. A .NET Gadgeteer nyílt forrású szoftver és ahhoz kapcsolódó hardvermodulok olyan kombinációját alkotja, amelyek segítségével bárki akár otthon, házi körülmények között is nekiállhat igen összetett, mégis egyszerűen programozható beágyazott eszközök fejlesztésének.

2011. július 22., péntek

Google Books .NET API bevetésen

Elég régóta küzdök azzal a problémával, hogy miként tudnám automatikusan rendszerezni az ebook gyűjteményemet. Természetesen itt most több ezer pdf, chm, djvu és doc/docx fájlra kell gondolni. Eddigi lusta megoldásom az volt, hogy próbáltam fő témakörök szerint „bemappázni” őket, de mindig is éreztem, hogy ez nem túl jó megoldás, mivel így lehetetlen rákeresni egy adott könyvre. Erre megoldást nyújthatott volna a Google Desktop Search alkalmazása, de sajnos ez sem volt elég hatékony számomra. Így hát nem maradt más, mint hogy magunk oldjuk meg ezt a problémát.

2011. június 26., vasárnap

Windows 8 szoftverfejlesztőknek: A Longhorn álom újraszületett?

[devPortal] Tegnap egy nagyon érdekes cikket olvastam az Arstechnica weboldalán a Windows 8-al kapcsolatosan. Annyira jónak találtam, hogy elhatároztam, lefordítom. Szerintem nagyon ott van a szeren, és rendkívül fontos hiányt pótol. Remélem, ti is sok hasznos információt találtok benne. A cikk szerzője Peter Bright.

2011. február 10., csütörtök

Silverlight 4 és C# könyvek magyarul és ingyenesen

Ha valaki alapvető programozási ismeretekkel (pl. C vagy Delphi) rendelkezik és szeretné kihasználni a legújabb Microsoft Silverlight 4 technológiát, akkor most egyebet sem kell tennie, mint letöltenie a devPortal.hu gondozásában született "Silverlight 4.0 - A technológia és ami mögötte van" című magyar nyelvű informatikai szakkönyvet.

2011. február 5., szombat

Prototípus (Prototype)

Megint a Factory-ból kell kiindulni ám ezúttal prototípusokat lehet gyártani. A cél, hogy előre inicializált típusokat lehessen menet közben készíteni úgy, hogy annak részletkérdéseivel már ne kelljen foglalkozni. A típusokat klónozással kell előállítani, szükség esetén mély másolással. (Amikor a referencia tagokat újra kell kreálni, hogy két objektum ne használja őket közösen). Sokszor előfordul az, hogy bizonyos osztályt prototípusként használunk fel. Ilyenkor általában az "új" típust származtatjuk a régiből és semmit sem változtatunk rajta, csak a konstruktorban inicializáljuk:
class MyObject
{
int size;

public MyObject(int size)
{
this.size = size;
}
}

class MyOtherObject : MyObject
{
public MyOtherObject()
: base(22)
{ }
}
Ezzel tulajdonképpen semmit sem csináltunk, nem fejlesztettük tovább az objektumot, csak létrehoztunk egy prototípus osztályt. Ez főleg akkor válik nehézkessé, amikor ebből kellene 30-40 féle. Többek között az ilyen esetekre jó a Prototípus használata.

Alkalmazhatóság

Akkor használjuk a Prototype mintát, ha rendszernek függetlennek kell lennie a termék létrehozási, összeállítási és megjelenítési módjától; és
  • ha a példányosítandó osztályok futásidőben határozódnak meg, például dinamikus betöltés esetén, vagy
  • el szeretnénk kerülni a termékek osztályhierarchiájával párhuzamos osztályhierarchiájú gyárak építését, vagy
  • amikor az osztályok példányainak csak néhány különböző állapotkombinációja lehet. Kényelmesebb a megfelelő számú prototípust elhelyezni és klónozni, mint példányosítani a megfelelő állapottal.
Struktúra


Résztvevők
  1. Protype: deklarál egy interface-t önmaga klónozásához.
  2. ConcretePrototype: implementál egy műveletet önmaga klónozásához.
  3. Client: új objektumot hoz létre, megkérve a prototípust önmaga klónozására.

Együttműködési információk

A kliens megkéri a a prototípust, hogy klónozza önmagát.

Következmények

A Prototype minta hasonló következményekkel jár mit az Abstract Factory és Builder: elrejti a termék osztályokat a kliens elől, csökkentve a ezzel azon nevek számát, amiket a kliens ismer. Emellett, ezek a minták lehetővé teszik, hogy a kliens módosítás nélkül dolgozzon az alkalmazás-specifikus osztályokkal.

A Prototype minta további előnyei:
  1. Termékek hozzáadása és eltávolítása futásidőben. Új konkrét termékosztály beépítése a rendszerbe, egyszerűen a prototípuspéldány bejegyeztetése a klienssel. Ez valamelyest hatékonyabb, mint a többi létrehozási minta, mert a kliens futásidőben építhet be és távolíthat el prototípusokat.
  2. Új objektumok meghatározása változó értékek mellett. A nagymértékben dinamikus rendszerek lehetővé teszik a új viselkedés definiálását az objektum összetételén keresztül – például az objektum változóinak értéket adva – és nem új osztályokat meghatározva. Ténylegesen a meglévő osztályok példányosításával, és ezek a prototípus kliens-objektumaiként bejegyeztetve adjuk meg. A kliens az új viselkedésmódot a felelősség prototípusra való átruházásával adhatja meg. Ez a tervezési mód lehetőséget ad új „osztályok” létrehozására programozás nélkül. Valójában a prototípus klónozása hasonló egy osztály példányosításához. A Prototype minta nagymértékben csökkenti a rendszer számára szükséges osztályok számát.
  3. Új objektum megadása változó struktúra mellett. Sok alkalmazás részekből és alrészekből építi fel az objektumokat. Kényelmi szempontok miatt, az ilyen alkalmazások lehetővé teszik az összetett, felhasználó által definiált struktúrák példányosítását.
  4. Kevesebb alosztály. A Prototype minta lehetőséget ad a prototípus klónozására, ahelyett, hogy egy gyártó metódust kérne meg új objektum létrehozására. Ezért nincs szükség Creator osztály hierarchiára.
  5. Alkalmazás dinamikus beállítása osztályokkal. Egyes futásidejű környezetek lehetővé teszik az osztályok dinamikus betöltését alkalmazásokba.
A Prototype minta legfőbb felelőssége, hogy mindegyik Prototype alosztálynak implementálnia kell a Clone műveletet, ami bonyolult lehet. Például nehéz, ha az adott osztály már létezik. Clone implementálása akkor is nehéz lehet, ha olyan objektumokat tartalmaznak, amelyek nem támogatják a másolást vagy körkörös hivatkozást tartalmaznak.

Implementációs rész

A Prototype implementációjánál tartsuk szem előtt a következőket:
  1. Prototype menedzser használata. Amikor egy rendszerben a prototípusok száma nem rögzített (tehát dinamikusan létrehozhatóak és megsemmisíthetőek), nyilván kell tartani a rendelkezésre álló prototípusokat. A kliensek maguk nem kezelik a prototípusokat, csak mentik és kiolvassák a nyilvántartóból. Egy kliens elkéri a prototípust a nyilvántartóból, mielőtt klónozná azt. Ezt nyilvántartót hívjuk Prototype menedzsernek. A prototype menedzser egy asszociatív tároló, ami egy adott kulcshoz tartozó prototípust ad vissza. Vannak műveletei a kulcshoz tartozó prototípus bejegyzésére és a bejegyzés érvénytelenítésére. A kliensek megváltoztathatják a nyilvántartót és tallózhatják azt futásidőben. Ez lehetővé teszi a klienseknek, hogy a rendszer leltárát kódírás nélkül felvegyék.
  2. A Clone művelet implementálása. A Prototype minta legnehezebb része a Clone művelet helyes implementálása. Ez kiváltképp bonyolult amikor az objektumstruktúra körkörös hivatkozásokat tartalmaz. Ha a rendszerben lévő objektumok rendelkeznek Save és Load műveletekkel, akkor ezek használhatóak a Clone alapértelmezett implementálásához, egyszerűen csak menteni kell az objektumot majd azonnal visszatölteni. A Save művelet menti az objektumot egy puffermemóriába, a Load egy másolatot hoz létre, rekonstruálva a objektumot a pufferből.
  3. Klónok inicializálása. Amíg néhány kliens tökéletesen elégedett a klónnal, ahogy van, mások az általuk választott értékkel akarják inicializálni néhány vagy az összes belső állapotát. Általában ezeket az értékeket nem lehet átadni a Clone műveletben, mert számuk prototípus-osztályonként változik. Néhány prototípusnak szüksége lehet több inicializáló paraméterre; másoknak egyre sincs szükségük. A Clone műveletben történő paraméterátadás eleve kizárná egy egységes klónozó interfész használatát. Lehetséges, hogy a prototípus osztályok már definiálnak műveleteket az állapot kulcsrészeinek (újra) beállítására. Ha ez van, akkor a kliensek ezeket a műveleteket használhatják azonnal klónozás után. Ha nincs, akkor lehet nekünk kell az Initialize műveletet bevezetni, amely argumentumként kapja meg az inicializáló paramétereket és ennek megfelelően állítja be a klón belső állapotát.
Minta kód
// Prototype pattern
using System;
using System.Collections.Generic;

namespace Prototype
{
///
/// MainApp startup class for Real-World
/// Prototype Design Pattern.
///

class MainApp
{
///
/// Entry point into console application.
///

static void Main()
{
ColorManager colormanager = new ColorManager();

// Initialize with standard colors
colormanager["red"] = new Color(255, 0, 0);
colormanager["green"] = new Color(0, 255, 0);
colormanager["blue"] = new Color(0, 0, 255);

// User adds personalized colors
colormanager["angry"] = new Color(255, 54, 0);
colormanager["peace"] = new Color(128, 211, 128);
colormanager["flame"] = new Color(211, 34, 20);

// User clones selected colors
Color color1 = colormanager["red"].Clone() as Color;
Color color2 = colormanager["peace"].Clone() as Color;
Color color3 = colormanager["flame"].Clone() as Color;

// Wait for user
Console.ReadKey();
}
}

///
/// The 'Prototype' abstract class
///

abstract class ColorPrototype
{
public abstract ColorPrototype Clone();
}

///
/// The 'ConcretePrototype' class
///

class Color : ColorPrototype
{
private int _red;
private int _green;
private int _blue;

// Constructor
public Color(int red, int green, int blue)
{
this._red = red;
this._green = green;
this._blue = blue;
}

// Create a shallow copy
public override ColorPrototype Clone()
{
Console.WriteLine(
"Cloning color RGB: {0,3},{1,3},{2,3}", _red, _green, _blue);

return this.MemberwiseClone() as ColorPrototype;
}
}

///
/// Prototype manager
///

class ColorManager
{
private Dictionary _colors =
new Dictionary();

// Indexer
public ColorPrototype this[string key]
{
get { return _colors[key]; }
set { _colors.Add(key, value); }
}
}
}
Ismert felhasználás
  • Eszköztáraknál
  • Alapvető plug-in mechanizmusoknál
  • EJB/COM Szervereknél
Kapcsolódó minták

A Prototype és az Abstract Factory bizonyos tekintetben konkurens minták. Azonban használhatóak együtt is. Az Abstract Factory tárolhat egy olyan prototípus halmazt, amelyet klónozni akarunk, és a termék objektumokat adjuk vissza.

Felhasznált anyagok

2011. január 27., csütörtök

Építő (Builder)

Egy újabb olyan minta, ami a Factory-ból következik, ám attól picit eltérő a felhasználási módja. Az Építő (Builder) célja az, hogy a megfelelő elemeket menet közben építse fel különböző ábrázolási módszerekkel. Egy összetett objektum szerkezetének függetlenítése az ábrázolásától, így ugyanazzal az építési folyamattal különböző ábrázolásokat hozhatunk létre.

Motiváció

Egy RTF (Rich Text Format) dokumentumcsere formátum olvasónak képesnek kell lennie az RTF számos egyéb szövegformátumba való konvertálásra. Az olvasó átalakíthatja sima ASCII szöveggé vagy egy interaktívan szerkeszthető szövegeszközzé. A probléma mégis az, hogy a lehetséges konverziók száma végtelen. Így egyszerűen, az olvasó változtatása nélkül kell az új átalakítást megadni.
Egy megoldás, ha az RTFReader osztályt egy más szövegmegjelenítésre konvertáló TextConverter objektummal állítjuk be. Amikor az RTFReader elemzi az RTF dokumentumot, a TextConvertert használja az átalakítás végrehajtására. Amikor az RTFReader felismer egy RTF tokent (sima szöveget vagy RTF vezérlő szót), kiad egy kérelmet a TextConverter-nek, hogy alakítsa át az adott tokent. A TextConverter objektumok felelnek az adatkonverzióért és a token egy bizonyos megjelenítési formátumáért.
A TextConverter alosztályai specializálódhatnak különböző konverziókra és formátumokra. Például a az ASCIIConverter elutasíthat minden konverziós felkérést, kivéve a sima szövegeseket. A TeXConverter megvalósíthatja azokat a műveleteket, amelyek a TeX formátumban való, a szöveg minden stílusinformációját tartalmazó megjelenítésre vonatkozó kérelmekkel kapcsolatosak. A TextWidgetConverter olyan felhasználói felületet hozhat létre, amivel a felhasználó láthatja és szerkesztheti a szöveget.
Mindegyik konvertáló osztály-típus tartalmazza az összetett objektumok létrehozásához és összeállításához szükséges mechanizmusokat, és ezeket egy absztrakt interfész mögé rejti. Az átalakító független az olvasótól, amely az RTF dokumentum elemzéséért felelős.
A Builder minta magába foglalja mindezen kapcsolatokat. A mintában minden konverter osztályt builder-nek, és minden olvasót director-nak hívnak. A példát alkalmazva, a Builder minta elkülöníti a szöveges formátumot értelmező algoritmust (ez az, ami az RTF dokumentumokat elemezi) az átalakított formátum létrehozásától és megjelenítésétől. Lehetővé teszi számunkra, hogy az RTFReader elemző algoritmusát újra felhasználjuk különböző szövegmegjelenítés létrehozására RTF dokumentumokból – csak más TextConverter alosztályt kell beállítani az RTFReader számára.

Alkalmazhatóság

Akkor alkalmazzuk a Builder mintát, ha
  • Az összetett objektum létrehozási algoritmusának függetlennek kell lennie az objektumot felépítő részektől és azok összeállítási módjától.
  • A létrehozási folyamatnak lehetővé kell tennie a létrehozott objektum különböző megjelenítését.
Struktúra


Résztvevők

  • Builder (TextConverter) meghatározza a Product objektum létrehozására szolgáló absztrakt interfészt
  • ConcreteBuilder (ASCIIConverter, TeXConverter, TextWidgetConverter) a Builder interfészt megvalósításánál felépíti és összeállítja a termék részeit. Definiálja és nyomon követi az általa készített megjelenítési módokat. Interfészt biztosít a termék beolvasásához (pl.: GetASCIIText, GetTextWidget)
  • Director (RTFReader) a Builder interfész használatával létrehoz egy objektumot.
  • Product (ASCIIText, TeXText, TextWidget) a felépítendő összetett objektumot képviseli. ConcreteBuilder felépíti a termék belső ábrázolását és definiálja az összeállítási folyamatokat. Tartalmazza az alkotó elemeket defináló osztályokat, beleértve a részek összeállítására szolgáló interfészeket.

Együttműködési információk

  • A kliens létrehozza a Director objektumot és beállítja a kívánt Builder objektummal.
  • A Director értesíti a builder-t, ha egy objektum részét fel kell építeni.
  • A Builder kezeli a dircetor-tól érkező kérvényeket és a részeket ad a product-hoz.
  • A kliens elkéri a product-ot a builder-től.


A fenti diagram a Builder és a Director együttműködését szemlélteti a klienssel.

Következmények

A Builder minta föbb következményei:
  1. Lehetővé teszi a termék belső ábrázolásának a megváltoztatását. A Builder objektum a director számára egy absztrakt interfészt biztosít a termék felépítéséhez. Az interfész lehetővé teszi a builder-nek, hogy elrejtse a termék ábrázolását és belső felépítését. Elrejti a termék összeállításának módját is. Mivel a termék felépítése egy absztrakt interfészen keresztül történik, elég a termék belső ábrázolását megváltoztatni egy új builder típus definiálásához.
  2. Elszigeteli a felépítési és az összeállítási kódot. A Builder minta a modularitást az összetett objektum felépítésének és megjelenítésének az egységbe zárásával javítja. A kliensnek nem kell tudnia semmit a termék belső szerkezetét definiáló osztályokról; ilyen osztályok nem jelennek meg a Builder interfészében.
  3. Az építési folyamat finomabb vezérlését teszi lehetővé. Azoktól a létrehozási mintáktól eltérően, amelyek egy lépésben építik fel a terméket, a Builder minta lépésről lépésre építi fel a terméket a director felügyelete mellett. Csak akkor veszi át a director a terméket a builder-től, ha az elkészült vele. Emiatt a Builder interfész jobban tükrözi a termék felépítésének folyamatát, mint a többi létrehozási minta. Finomabb vezérlést tesz lehetővé az építési folyamatokon és ennek következtében a végső termék belső szerkezetén.

Implementációs rész

Tipikusan van egy absztrakt Builder osztály, ami minden egyes komponens működését definiálja, amelyek létrehozására a director megkéri. Alapértelmezésben a műveletek nem tesznek semmit. A ConcreteBuilder osztály felülbírálja azon komponensek műveleteit, amelyeknek létrehozásában érdekelt.
  1. Összeállítási és építési interfész. A Builder-ek termékeiket lépésről lépésre készítik el. Emiatt a Builder osztály interfésznek elég általánosnak kell lennie, hogy lehetővé tegye mindenfajta konkrét builder-nek a termékek létrehozását. A fő tervezési probléma az építési- és összeállítási folyamatok modellje. Azok a modellek megfelelőek, amelyek a létrehozási kérelmek eredményeit egyszerűen hozzácsatolják a termékhez.
  2. Miért nem absztrakt osztályokat használunk a termékeknél? Általában a konkrét builder-ek termékei annyira eltérnek a reprezentációjukban, hogy csak keveset nyerünk azzal, ha egy általános szülőosztályt adunk a különböző termékekhez. Mivel rendszerint a kliens a megfelelő konkrét builder-rel állítja be a director-t, ezért tudja, hogy a Builder melyik konkrét alosztálya van használatban, és ennek megfelelően kezeli a termékeket.
  3. Üres metódusok alapértelmezésként a Builderben. Üres metódusokat definiálunk, hogy a kliensek csak azokat a műveleteket bírálják felül, amelyekben érdekeltek.

Minta kód

A kódban szereplő osztályok:
  • Builder: VehicleBuilder
  • ConcreteBuilder: MotorCycleBuilder, CarBuilder, ScooterBuilder
  • Director: Shop
  • Product: Vehicle
Megvalósítás:
// Builder pattern -- Real World example

using System;
using System.Collections.Generic;

namespace Builder
{
///
/// Builder Design Pattern.
///

public class MainApp
{
///
/// Entry point into console application.
///

public static void Main()
{
VehicleBuilder builder;

// Create shop with vehicle builders
Shop shop = new Shop();

// Construct and display vehicles
builder = new ScooterBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

builder = new CarBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

builder = new MotorCycleBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

// Wait for user
Console.ReadKey();
}
}

///
/// The 'Director' class
///

class Shop
{
// Builder uses a complex series of steps
public void Construct(VehicleBuilder vehicleBuilder)
{
vehicleBuilder.BuildFrame();
vehicleBuilder.BuildEngine();
vehicleBuilder.BuildWheels();
vehicleBuilder.BuildDoors();
}
}

///
/// The 'Builder' abstract class
///

abstract class VehicleBuilder
{
protected Vehicle vehicle;

// Gets vehicle instance
public Vehicle Vehicle
{
get { return vehicle; }
}

// Abstract build methods
public abstract void BuildFrame();
public abstract void BuildEngine();
public abstract void BuildWheels();
public abstract void BuildDoors();
}

///
/// The 'ConcreteBuilder1' class
///

class MotorCycleBuilder : VehicleBuilder
{
public MotorCycleBuilder()
{
vehicle = new Vehicle("MotorCycle");
}

public override void BuildFrame()
{
vehicle["frame"] = "MotorCycle Frame";
}

public override void BuildEngine()
{
vehicle["engine"] = "500 cc";
}

public override void BuildWheels()
{
vehicle["wheels"] = "2";
}

public override void BuildDoors()
{
vehicle["doors"] = "0";
}
}

///
/// The 'ConcreteBuilder2' class
///

class CarBuilder : VehicleBuilder
{
public CarBuilder()
{
vehicle = new Vehicle("Car");
}

public override void BuildFrame()
{
vehicle["frame"] = "Car Frame";
}

public override void BuildEngine()
{
vehicle["engine"] = "2500 cc";
}

public override void BuildWheels()
{
vehicle["wheels"] = "4";
}

public override void BuildDoors()
{
vehicle["doors"] = "4";
}
}

///
/// The 'ConcreteBuilder3' class
///

class ScooterBuilder : VehicleBuilder
{
public ScooterBuilder()
{
vehicle = new Vehicle("Scooter");
}

public override void BuildFrame()
{
vehicle["frame"] = "Scooter Frame";
}

public override void BuildEngine()
{
vehicle["engine"] = "50 cc";
}

public override void BuildWheels()
{
vehicle["wheels"] = "2";
}

public override void BuildDoors()
{
vehicle["doors"] = "0";
}
}

///
/// The 'Product' class
///

class Vehicle
{
private string _vehicleType;
private Dictionary _parts = new Dictionary();

// Constructor
public Vehicle(string vehicleType)
{
this._vehicleType = vehicleType;
}

// Indexer
public string this[string key]
{
get { return _parts[key]; }
set { _parts[key] = value; }
}

public void Show()
{
Console.WriteLine("\n---------------------------");
Console.WriteLine("Vehicle Type: {0}", _vehicleType);
Console.WriteLine(" Frame : {0}", _parts["frame"]);
Console.WriteLine(" Engine : {0}", _parts["engine"]);
Console.WriteLine(" #Wheels: {0}", _parts["wheels"]);
Console.WriteLine(" #Doors : {0}", _parts["doors"]);
}
}
}
Eredmény:


Ismert felhasználás

Különféle formátumok konvertálásához, fordítókban elemzőknél, normalizált adatbázisok „építéséhez”.

Kapcsolódó minták

Az Abstract Factory abban hasonlít a Builder-re, hogy az is összetett objektumokat építhet fel. Az elsődleges különbség, hogy a Builder minta az összetett objektumok lépésről lépésre való létrehozását helyezi középpontba. Az Abstract Factory esetében a termékobjektum-családok a hangsúlyosabbak (akár egyszerű, akár összetett). A Builder minta utolsó lépésben visszaadja a terméket, azonban az Abstract Factory minta, ami azt illeti azonnal.

Felhasznált anyagok

2011. január 14., péntek

Gyártó függvény(Factory Method)

Az Elvont Gyár mintához szorosan kapcsolódik a Gyártó Függvény minta, amivel magát az Elvont Gyárat lehet létrehozni, de implementálhatóak Protoype használatával is. Tehát az előzőekben bemutatottakban felül itt egy rövid kód erről a mintáról:
class Creator where T : new()
{
T Create()
{
return Activator.CreateInstance();
}
}
Ennek lényege a megfelelő objektumok létrehozás anélkül, hogy annak részleteivel foglalkozni kellene egy magasabb absztrakciós szinten.

DXCoder

Elvont Gyár (Abstract Factory)

A Gyár (Factory) célja, hogy objektumokat gyártson úgy, hogy a gyártás részleteivel a hívónak nem kell foglalkoznia. Az Elvont gyár (Abstract Factory) pedig egy felületet biztosít a gyárak számára a konkrét osztályok megadása nélkül, biztosítva így a továbbfejlesztési lehetőséget.

Alkalmazhatóság

Akkor használjuk az Abstract Factory mintát, ha
  • A rendszernek függetlennek kell lenni attól, hogyan hozza létre, állítja össze és jeleníti meg a termékeit.
  • A rendszernek be kell állítania egyet a termékcsalád közül.
  • Kapcsolódó termékobjektumok egy családját együttes használatra tervezték, és ezt a megszorítást ki kell kényszeríteni.
  • Gondoskodni kell a termékek osztálykönyvtáráról, és csak az interfészüket fedhetjük fel, de a megvalósításukat nem.
Struktúra


Résztvevők
  • AbstractFactory: az absztrakt termékobjektumokat létrehozó interfészt deklarálja.
  • ConcreteFactory: a konkrét termékobjektumok létrehozására szolgáló műveleteket implementálja.
  • AbstractProduct: a termékobjektumok egy típusának interfészt deklarálja.
  • ConcreteProduct: definiálja a létrehozandó termékobjektumot a megfelelő konkrét factory (gyár) mellett, implementálja az AbstractProduct interfészt.
  • Client: csak az AbstractFactory és az AbstractProduct osztályok által deklarált interfészeket használja.
Együttműködési információk

Normál esetben a ConcreteFactory osztály egyetlen példányát hozza létre futásidőben. Ez a konkrét factory egyéni implementációval rendelkező termékobjektumokat készít. Különböző termékobjektumok létrehozásához a kliensnek különböző konkrét factory-kat kell használnia. Az AbstractFactory a termékobjektumok létrehozását a ConcreteFactory alosztályra bízza.

Következmények

Az Abstract Factory mintának a következő előnyei és hátrányai vannak:
  1. Elszigeteli a konkrét osztályokat. Az Abstract Factory minta segít az alkalmazások által létrehozott objektumosztályok irányítását. Mivel a factory egységbe zárja a termékobjektum létrehozásának felelősségét és folyamatát, így elszigeteli a klienst az implementációs osztályoktól. A kliensek a példányokat saját absztrakt interfészükön át kezelik. A termékosztályok nevei elszigeteltek a konkrét factory implementációjában; azok nem jelennek meg a kliens kódjában.
  2. Megkönnyíti a termékcsaládok cseréjét. A konkrét factory osztálya egy alkalmazásban egyszer jelenik meg – ahol példányosítottuk. Ez megkönnyíti az alkalmazás által használt konkrét factory megváltoztatását. A különböző termék használatához csak a konkrét factory-t kell megváltoztatni. Mivel az absztrakt factory komplett termékcsaládokat készít, egyszere megváltozik az egész termékcsalád.
  3. Támogatja a termékek közti konzisztenciát. Amikor egy családon belül a termékobjektumokat együttműködésre tervezték, akkor fontos, hogy az alkalmazás egy időben csak egy családhoz tartozó objektumokat használjon. Az AbstractFactory ezt könnyen érvényesíti.
  4. Az új termékfajták támogatása nehézkes. Az absztrakt factory kibővítése újfajta termékek létrehozására nem könnyű. Azért mert az AbstractFactory interfész rögzíti a létrehozható termékek halmazát. Az újfajta termékek támogatásához ki kell bővíteni a factory interfészt, amihez meg kell változtatni az AbstractFactory osztályt és annak minden alosztályát.
Implementációs rész
  1. Factory-k mint Singleton-ok. Egy alkalmazásnak tipikusan csak egyetlen egy ConcreteFactroy példányra van szüksége termékcsaládonként. Így azokat általában legjobb Singleton segítségével implementálni.
  2. A termékek létrehozása. Az AbstractFactory csak a termékek létrehozására szolgálóinterfészt deklarálja. A ConcreteProduct alosztály végzi valójában a létrehozást. A legáltalánosabb megoldás, ha minden termékhez deklarálunk egy factory metódust. Amíg az implementáció egyszerű, addig minden egyes termékcsaládhoz szükség van egy konkrét factory alosztályra, még akkor is ha a termékcsaládok csak alig térnek el.
  3. Ha sok termékcsaládra van szükség, a konkrét factory implementálható a Prototype minta felhasználásával. A konkrét factory a családba tartozó minden termékhez külön tartozó prototípus példány segítségével inicializálható, és a prototípus klónozásával hozható létre új termék. A prototípus alapú megközelítés kiküszöböli, hogy minden egyes új termékcsaládnak új konkrét factory-ra lenne szüksége.
  4. ővíthető factory-k definiálása. Az AbstractFactory általában különböző műveleteket definiál az általa létrehozható minden termékfajtához. A termékek típusa a műveletek szignatúrájába van kódolva. Egy új terméktípus hozzáadása estén szükség van az AbstractFactory interfész és az összes tőle függő osztály megváltoztatására.
  5. Egy rugalmasabb, de kevésbé biztonságos kivitelezés, ha az objektumokat létrehozó műveleteket paraméterezzük. Ez a paraméter határozza meg a létrehozandó objektum típusát. Ez lehet egy osztályazonosító, egy egész szám, egy sztring, vagy bármi ami azonosíthatja a termék típusát. Ezzel a megközelítéssel, az AbstractFactory-nak csak egy egyszerű „Make” műveletre van szüksége, ami a létrehozandó objektum típusát jelző paraméterrel rendelkezik. Ezt a technikát használjuk a fent megbeszélt Prototype- és osztály-alapú absztrakt factory-kban.
Példakód

Az elvont gyár minta segítségével most létrehozunk egy pályaszerkesztőt DXCoder példája alapján. Alaposztályok a megjelenítéshez:
class Sprite 
{
public Sprite(int size, string textureName) { }
}

static class SpriteFactory
{
public static Sprite CreateBall()
{
return new Sprite(20, "ball.png");
}

public static Sprite CreateWall()
{
return new Sprite(100, "wall.jpg");
}
}

class Game
{
Sprite[,] map = new Sprite[10, 10];

public void CreateLevel()
{
map[0, 0] = SpriteFactory.CreateWall();
map[1, 0] = SpriteFactory.CreateWall();
map[1, 1] = SpriteFactory.CreateBall();
}
}
Mint látható a Game osztályban már nem kell foglalkozni az egyes elemek részleteivel. A CreateLevel helyére el lehet képzelni egy olyan ciklus, ami egy file-ból tölti be a pályát. A másik trükk, hogy felhasználtuk az Egyke mintát is egyúttal.
Mi van akkor, ha a pályaszerkesztővel több pályát is szeretnénk készíteni egységes jelöléssel? A wall az mindig W, a ball az pedig mindig B. Többféle világ is van, ahol a ball az lehet forest, vagy robo is. Nem szeretnénk átírni se az editort, se a pálya betöltőt, úgyhogy a gyárak körül kell változtatni. A következő példa már az Elvont gyárat mutatja be:
  • AbstractFactory: IAbstractFactory
  • ConcreteFactory: RobotSpriteFactory, ForestSpriteFactory
  • Client: Game (CreateLevel)
Forrás:
using System;

namespace ConsoleApp
{
static class SingletonManager where T : new()
{
static T instance = Activator.CreateInstance();

public static T Instance
{
get { return instance; }
}
}

class Sprite
{
public Sprite(int size, string textureName) { }
}

interface IAbstractFactory
{
Sprite CreateBall();
Sprite CreateWall();
}

class ForestSpriteFactory : IAbstractFactory
{
public Sprite CreateBall()
{
return new Sprite(20, "ball.png");
}

public Sprite CreateWall()
{
return new Sprite(100, "wall.jpg");
}
}

class RobotSpriteFactory : IAbstractFactory
{
public Sprite CreateBall()
{
return new Sprite(26, "roboBall.png");
}

public Sprite CreateWall()
{
return new Sprite(120, "roboWall.jpg");
}
}

class Game
{
Sprite[,] map = new Sprite[10, 10];

public void CreateLevel(IAbstractFactory factory)
{
map[0, 0] = factory.CreateWall();
map[1, 0] = factory.CreateWall();
map[1, 1] = factory.CreateBall();
}
}

static class Program
{
static void Main()
{
var forestGame = new Game();
forestGame.CreateLevel(SingletonManager.Instance);

var robotGame = new Game();
robotGame.CreateLevel(SingletonManager.Instance);
}
}
}
Látható, hogy a CreateLevel lényegében semmit nem változott. Ha több 100 elemet kell betölteni, akkor se történik változás, mindig a világnak megfelelő objektumok fognak betöltődni. Itt még pluszban az Egyke minta egyik C# változatát is felhasználtuk, hogy ne kelljen állandóan fölösleges objektumokat létrehozni.

Kapcsolódó minták

Az Abstact Factory osztályokat gyakran factory metódusokkal (gyártó függvényekkel) valósítják meg, de implementálhatóak Protoype használatával is.

Felhasznált anyagok

2011. január 9., vasárnap

Alphabetical BubbleSort

Néha jól jön egy rendező algoritmus, ami egy string tömb elemeit abc sorrendbe rakja. Itt egy lehetséges megvalósítás:
private string[] AlphabeticalBubbleSort(string[] values)
{
int i = 0;
int j = 0;
string temp;

for (i = values.GetUpperBound(0); i >= 0; i--)
{
for (j = 0; j <= i; j++)
{
if (values[j].ToString().ToLower().Trim() > values[j + 1].ToString().ToLower().Trim())
{
//Swap values
temp = values[j].ToString();
values[j] = values[j + 1];
values[j + 1] = temp;
}
}
}
return values;
}

C# tömbök mesteri szinten (2)

Egy picit még elidőzünk a múlt héten felvetett téma mellett. Nyilvánvaló, hogy a tömbök, főleg ha ciklusokon belül használjuk őket néha igen komoly teljesítményproblémát jelenthetnek, melyek egy része magától értetődő, más része rejtett. Az alábbiakban egy-két jellemző példát lehet majd látni erre.
Tételezzük fel, hogy egy táblázatot alkotó "jagged" tömb minden egyes sora kb. 4000 bájt méretű (figyelem, a méret a lényeg!). Ha ez a táblázat túl sok sorból áll, akkor minden esetben, amikor egy eltérő sor adataihoz hozzá szeretnénk férni, akkor a tábla/tömb és az állandó memória lapméretek miatt (mondjuk 4 KB) az operációs rendszernek úgynevezett memória-lapváltást kell végrehajtania. Másképp mondva hozzá kell férnie a tömbelem másik lapon lévő adataihoz. Vagyis a lenti példa szerint a belső ciklus miatt, minden tömbelemhez való hozzáférés újabb és újabb plusz memóriaműveletet (a már említett lapváltást) generál. Példa:

for (int column = 0; column < MAX_COLUMNS; column++)
{
for (int row = 0; row < MAX_ROWS; row++)
{
table[row][column] = BlankTableElement();
}
}
A fenti példa szerint tehát MAX_COLUMNS * MAX_ROWS darab memória-lapváltás történik, melyet mindenképp jó lenne kiküszöbölni, és erre az alábbi lehetőség áll a rendelkezésünkre.

for (int row = 0; row < MAX_ROWS; row++)
{
for (int column = 0; column < MAX_COLUMNS; column++)
{
table[row][column] = BlankTableElement();
}
}
Egyszerűen megcserélődött a két ciklus, vagyis ami eddig kívül volt az belül lesz és fordítva. Ebben az esetben is lesz memória-lapváltás, de már csak MAX_ROWS és nem MAX_COLUMNS * MAX_ROWS esetben. A teljesítménynövekedés feladattól és környezettől függően változhat, de bizonyos esetben, pl.: a második példa ciklusa akár 1000x előbb is befejeződhet, mint az első. Ha már a tömböknél tartunk... Az alábbi példa igaz, hogy a kód olvashatóságát rontja egy kissé, de teljesítmény szempontból, bizonyos helyzetekben rendkívül hatékony tud lenni (ez az úgynevezett „unrolling” művelet). Példa:

i = 0;
while (i < count)
{
a[i] = i;
i = i + 1;
}
Ez egy egyszerű ciklus volt, ami egy „a” numerikus tömböt feltöltött az „i” index értékével. A fenti megoldás helyett, amennyiben lehetséges (nyilván nem mindig), legyen inkább egy másik megoldás…

i = 0;
while (i < count - 1)
{
a[i] = i;
a[i + 1] = i + 1;
i = i + 2;
}
if (i == count)
{
a[count - 1] = count - 1;
}
A lényeg az, hogy a tömbön belül nem egyesével, hanem fura módon kettesével léptetjük az „i” indexet, és minden iterációnál kitöltjük az aktuális, valamint az aktuális i + 1 tömbelemet (vagy más feladat esetén végrehajtunk valami más tömbműveletet). Páratlan számú iteráció esetén az utolsó elem kitöltésére csak a ciklus után, külön kerülhet sor. A teljesítménynövekedés környezettől függően változhat, ezért az alkalmazása előtt méréseket kell végezni. Ahol a teljesítmény különösen kritikus és fontos, ott a .NET menedzselt kód által nyújtott plusz szolgáltatások kikerülhetők, azaz elérhető, hogy a tömbök adata ne a referencia típusokra jellemző menedzselt halmon (heap), hanem az annál jóval gyorsabban hozzáférhető, és a szemégyűjtő Garbage Collector által nem inzultált vermen (stack) tárolódjon. Az imént említett tömbhasználat felturbozásához a stackalloc nevű C# utasítást kell használni (fordításkor a /unsafe opció szükséges). Ez a módszer csak egy dimenziós, 0 bázisú, érték típusú elemeket tartalmazó tömböknél működhet. Amikor a stackalloc használatban van, akkor a memória blokkok eléréséhez úgynevezett unsafe pointer-t alkalmaz a rendszer. Ezek a memóriaterületek az adott hatókör (pl.: egy metódus return utasítása) után automatikusan felszabadulnak. Példa:

class StackallocSample
{
unsafe public static void ShowData(int* pArray)
{
for (int* ip = pArray; ip < (pArray+5); ip++)
{
Console.WriteLine("value {0} at address: {1}", *ip, (int)ip);
}
}

static void Main(string[] args)
{
unsafe
{
int* pArray= stackalloc int[5];
pArray[0] = 12;
pArray[1] = 34;
pArray[2] = 56;
pArray[3] = 78;
pArray[4] = 90;
ShowData(pArray);
}
}
}
Minden téma végén megjegyzem, hogy ezeket a megoldások és példák természetesen nem az én fejemben születtek. Ahhoz, hogy az ember hozzájusson a fenti okosságokhoz, egyszerűen csak olvasni kell, méghozzá sokat. "Mert olvasni nem gyíkság..."

Iteráció teljesítménynövelés

A múltkori témák adták az ötletet arra nézve, hogy néhány további, teljesítménynövelésre buzdító javaslatot tegyek, de most inkább csak az iterációkra (ciklusokra) vonatkoztatva. A korábbi példákhoz nagyon hasonló eset az, amikor az egymásba ágyazott ciklusok önmagában álló teljesítményét vizsgáljuk. Az utasítások működési jellemzőjéből adódóan, a belső ciklus minden egyes iterációjának alkalmával a ciklusváltozót inicializálni kell, az összehasonlító logikai kifejezést értékelni kell, valamint ciklusváltozót is növelni kell.

for (int column = 0; column < 100; column++)
{
for (int row = 0; row < 5; row++ )
{

}
}
A fenti példában kívül van a több, belül pedig a kevesebb iterációt végrehajtó ciklus. Ezesetben a ciklusváltozók menedzselése egészen pontosan 100 + (100 * 5) = 600 alkalommal hajtódik végre, ami kiélezett helyzetben tetemes időt vehet igénybe. Ilyenkor nincs más teendő, mint a külső, többször iteráló, illetve a belső, kevesebbszer iteráló ciklust egyszerűen felcserélni egymással.

for (int row = 0; row < 5; row++ )
{
for (int column = 0; column < 100; column++)
{

}
}
A javított változatban a ciklusváltozók menedzselése 5 + (5 * 100) = 505 alkalommal hajtódik végre, ami a 16%-kal kevesebb feladatot ró a működtető környezetre (ez utóbbi példa javára). Természetesen .NET esetén ez a példa picit erőltetett, mivel a JIT fordító számos optimalizálást már előre végre tud hajtani. A végső megvalósítás előtt ezesetben is érdemes méréseket végezni.
Szintén ciklusok esetén jellemző az, amikor mondjuk egy metóduson belül lévő ciklusban végre kell hajtani bizonyos műveleteket, aztán később ugyanilyen cikluson belül egy másik műveletet. Ezesetben érdemes megvizsgálni annak a lehetőségét, hogy a két azonos feltételekkel rendelkező ciklust nem lehetne-e összevonni (ezek az úgynevezett „jamming” vagy „fusion” műveletek).

for (int i = 0; i <= 100; i++)
{
employeeName[i] = "";
}
// Itt most különféle egyéb utasításokat, műveleteket kell elképzelni…
for (int i = 0; i <= 100; i++)
{
employeeEarnings[i] = 0;
}

A fenti megoldás helyett, amennyiben ez lehetséges, legyen inkább összevonás…

for (int i = 0; i <= 100; i++)
{
employeeName[i] = "";
employeeEarnings[i] = 0;
}
A ciklusokon belüli összetett műveletek mértékét a lehető legkisebbre kell venni. Ez azt jelenti, hogy minden idő és erőforrásigényes utasítás esetén meg kell vizsgálni, hogy nem lehet-e azt kiemelni a cikluson kívülre.

for (int i = 0; i < rateCount; i++)
{
netRate[i] = baseRate[i] * rates.CalculateDiscounts();
}
Ha a „rates” objektum „CalculateDiscounts()” metódusa mondjuk adatbázis, vagy más erőforrásigényes műveletet hajt végre, aminek a végeredménye az adott időben ugyanaz, akkor érdemes kiemelni azért, hogy csak egyszer hajtódjon végre (tudom, hogy ezek elemi dolgok, de hátha olyasvalaki is olvassa, aki csak most kezdi a programozói pályáját). Tudom-tudom... ezek primitív dolgok, de akkor is.

quantityDiscount = rates.CalculateDiscounts();
for (int i = 0; i < rateCount; i++)
{
netRate[i] = baseRate[i] * quantityDiscount;
}
Amennyiben egy cikluson belül már nincs értelme a további iterációnak, akkor gondoskodni kell az iteráció azonnali, vagy legalábbis a lehető leghamarabb történő befejezéséről. Minden további felesleges műveletet teljesítmény és erőforrás gazdálkodási okokból kerülni kell.
Put About

2011. január 6., csütörtök

C# tömbök mesteri szinten (1)

Még régebben találtam rá Put About tömbökkel foglalkozó írására. Mivel az ott leírtakat nemigen lehet megtalálni egyetlen egy magyar nyelvű C#-os könyvben sem, úgy gondoltam, hogy most itt a blogomban is megjelenítem, remélve azt, hogy így még több emberhez eljuthat ez a remek írás.

Tömbölés - a kezdet

Elismerem, picit fura a cím, tegyük tisztába hát. A "Tömbölés" szót ebben az értelemben én találtam ki, és magyarról magyarra fordítva valami "tömbök kezelésének érdekességei programozási nyelvekben"-féleséget kell érteni. Az ötletet, hogy egy picit foglalkozzunk a témával két remek könyv ihlette. Ezek kapcsán gondoltam úgy, hogy bizonyos jelenségek és problémák, melyeket majd leírok (és amelyek nem az én agyszüleményeim), minden programozó számára hasznosak lehetnek a jövőben. Elöljáróban annyit, hogy nem igazán szeretem a tömböket. A múltban, amikor még Fortran, Basic vagy Turbo Pascal nyelvek töltötték ki a napjaimat, más lehetőség hiányában én is intenzív tömbhasználó voltam, aztán kb. 15 éve, amikor az első igazi relációs adatbázis közelébe kerültem, teljesen elbűvölt az itt fellelhető adatszerkezetek, adattárolási (meg egyéb) lehetőségek tárháza. Azóta kerülöm a tömböket bár elismerem, néha napján bizony jól jönnek, sok esetben meg nem is lehet meglenni nélkülük. A következőkben elsősorban az általam jobban ismert .NET alapon létező, C# nyelven készített tömbökről beszélek (a többi platformon vagy nyelven elérhető tömböktől ezúton is bocsánatot kérek).
Minden tömb a System.Array absztrakt osztályból származik, melynek direkt őse a System.Object. Ez egyben azt is jelenti, hogy tömb referencia típus, és így az úgynevezett menedzselt halom (managed heap) területen foglal helyet. Alapvetően háromféle tömb létezik: az egy dimenziós (vektor), a több dimenziós (mátrix, kocka, stb.), illetve a szabálytalan (tömböket tartalmazó tömb, vagy hivatalos nevén "jagged") tömb. Minden tömb deklaráció egy hivatkozás (referencia) a hozzá tartozó, adott típusú adatokra. A tömbök elemeire való hivatkozás (tömb index) alapján történik, amely alapértelmezés szerint 0-tól indul, de persze ha úri kedvünk úgy tartja, ettől eltérő is lehet. Példák:
int[] egeszvektor;          
// Deklaráltunk egy hivatkozást egy tömbre
egeszvektor = new int[100];
// Létrehoztunk egy 100 elemből álló tömböt.
int[,] egeszmatrix = new int[10, 20];
// Deklaráltunk és létrehoztunk egy 10x20-as mátrixot
int[,,] egeszkocka = new int[5, 4, 3];
// Deklaráltunk és létrehoztunk egy 5x4x3-as kockát
Point[][] polygonok = new Point[3][];
// Deklaráltunk és létrehoztunk egy poligont (jagged array)
A tömböket automatikusan vagy kényszerítve, de át lehet alakítani egyik típusból a másikba. Példák:
FileStream[,] fs = new FileStream[5, 10];
// Létrehozunk egy 5x10-es fájl stream típusú mátrixot
object[,] fso = fs;
// Automatikus típuskonverzióval beletesszük egy object tömbbe
Stream[] st = (Stream[])fso;
// Típuskényszerítést (kézi konverziót) hajtunk végre egy Stream-re
Az Array típusnak számos jellemzője (property) és metódusa van, melyek jól használhatók a munka közben. Példa:

Array.Copy(tomb1, tomb2, tomb1.Lenght);
// Tömb másolása

Ja igen, ez tényleg olyan unalmas, hogy már magam is vattát köpök. Rendben, akkor elemi iskolai magyarázatokból ennyi elég is volt. Nem áll szándékomban programozói tanfolyamot indítani, úgyhogy lépjünk tovább az érdekesebb, főleg nem várt teljesítményproblémákat felvető részek felé.
Az Array.Copy() metódus nem az eszeveszett sebességéről híres, épp ezért megfontolandó, hogy nem érdemesebb-e helyette időnként a System.Buffer.BlockCopy() metódusát használni. Ez, a benne alkalmazott bájt-eltolási módszer miatt lényegesen gyorsabb az előzőleg említett "kollégájánál", azonban csak az egyszerű (primitive) típusú tömbök másolását támogatja, és nem ajánlja fel ugyanazon konverziós lehetőségeket, minta sima Array.Copy(). Elsősorban bájt vagy karaktersorozatokat tartalmazó tömbökhöz célszerű használni. A BlockCopy()-ról részletek itt olvashatók.
Amennyiben igazán biztonságos másolásra vágyunk, akkor a System.Array.ConstrainedCopy() metódus a mi emberünk. Ez ugyanis garantálja, hogy a másolási művelet teljesen szabályos legyen, ellenkező esetben kivételt fog dobni. Sajnos a forrás tömb csak ugyanolyan típusú lehet (vagy ugyanannak a leszármazottja), mint a célként meghatározott tömb. A ConstrainedCopy()-ról részletek itt olvashatók.
A tömbök mindegyike automatikusan (implicit módon) megvalósítja az IEnumerable, ICollection, és IList interfészeket, a velük járó lehetőségekkel együtt. Azonban a nevezett interfészek generikus változatainak a megvalósítását a CLR (Common Language Runtime) csapat nem hajtotta végre, hivatalosan elsősorban a több dimenziós és nem 0 kezdetű tömbök kellemetlenségei miatt. Ugyanakkor egy kis trükkel futás közben mégiscsak belecsempészték a generikus interfészeket, de ezek elmagyarázása sajnos túl sokáig tartana, úgyhogy egyelőre legyen elég annyi, hogy a fentebb említett interfészek generikus változatai nincsenek ugyan, de mégis vannak.
Egy egydimenziós, 0 kezdetű (0 bázisú, vagy 0 kezdő indexet tartalmazó) tömb kezelése lényegesen gyorsabb a nem 0 bázisú és/vagy többdimenziós tömbökhöz képest. Ennek egyik oka az, hogy az egydimenziós, 0 bázisú tömbre külön IL (Intermediate Language) utasítások vannak, úgy mint: newarr, idelem, idelema, stelem. Ezek felhasználásával a JIT fordító képes remekül optimalizálni a műveleteket. Pl.: a 0 bázisú, egydimenziós tömbök eltolását (memóriabeli elhelyezkedésük címét) egyszerű műveletekkel képes kiszámolni, míg a nem 0 bázisú tömböknél viszont különféle osztásokat is kell még végeznie. A másik ok az, hogy a 0 bázisú tömböknél a JIT fordító képes az index tartomány ellenőrzést előre, pl.: még a ciklus végrehajtása előtt elvégezni. Mondanom se kell, hogy ezzel bizony majdhogynem fénysebességre gyorsulhat a művelet. Példa:

int[] a = new int[5];
for (int index = 0; index < a.Length; index++)
{
// Itt valamit csinálni kellene…
}

A ciklus fejben lévő index < a.Length jellemző értékének vizsgálata valójában a háttérben egy metódushívás, ami az "a" tömb hosszát kérdezi le. Ez az információ azonban már ismert a ciklus megkezdése előtt is, így tehát a JIT fordító képes előre megvizsgálni, majd egy munkaváltozóba félretenni ezt az értéket még a végrehajtás előtt azért, hogy a "a.Lenght" hívásra később (a ciklus végrehajtása során) már ne kerüljön sor. Tanúság: ennek a hatására a ciklus végrehajtás bizony sokkal gyorsabb lesz. A másik tanúság az, hogy a programozó ne akarjon "okos" kódot írni, azaz ne akarjon okosabb lenni a JIT fordítónál és látván a helyzetet, előre kiemelni egy változóba az "a.Length" értékét, majd azt beletenni a ciklus fej részébe. Ez, eredményét tekintve persze ugyanolyan jó megoldás, mint amit a JIT fordító választ, csak az a baj, hogy teljesen felesleges, és rontja a kód olvashatóságát (mivel a JIT fordító is megtenné ugyanezt helyettünk). Nagyon fontos, hogy egy programozó tudja, miképp viselkedik az a környezet, ahol tevékenykedik, mert így legalább később egy csomó, csúnyácska megoldást elkerülhet.
Ugyanennél a példánál maradva képes a JIT fordító előre leellenőrizni, hogy lesz-e tömbhatár túllépés vagy sem. Érdekes mi? Egy még meg nem történt dolgot előre látni? Ahh, "akkor ezt itt most, hogy?" (az idézet nem tőlem van). Hát úgy, hogy bizony a JIT mindent előre tud. Először is a tömb kezdő indexe 0, vagyis ez ellenőrizhető. A legmagasabb index vizsgálatra pedig a következő ((a.Length - 1) <= a.GetUpperBound(0)) kifejezés alkalmas. Ez is előre elvégezhető. Márpedig ha ez mind igaz (mármint hogy ha a JIT fordító előre tudja, hogy bizony nem lesz tömbhatár túllépés), akkor a teljes ciklus végrehajtási kódjából kihagyhatja az erre vonatkozó ellenőrzési utasításokat. Ettől kezdve a mi kis ciklusunk hiperűrsebességre kapcsolhat, és olyan gyorsan befejeződik, hogy időnk se lesz rá gondolni többet. A fentebb leírtak is alátámasztják azt a jóslatot, miszerint: a menedzselt, JIT fordítóval generált kódé lesz a jövő. Milyen igaz. Sajnos a nem 0 bázisú és többdimenziós tömböknél a JIT fordító nem képes előre elvégezni a tömbhatár túllépési ellenőrzéseket (legalábbis a .NET 2.0-ig ez így volt), ami azt jelenti, hogy ezek a részek továbbra is a cikluson belül maradnak, vagyis az ellenőrzés a tömbelemre való tényleges hivatkozáskor fog megtörténni. Még további gyorsítási tippek, trükkök is léteznek, de erről majd máskor.
Put About

2011. január 4., kedd

A .NET újraindítása

Ma már a .NET nem csak a fejlesztőknek mond valamit, hanem a felhasználóknak is, hiszen egyre több alkalmazást fejlesztenek ebben a nagyszerű keretrendszerben. De sajnos itt sem minden tökéletes, hiszen .NET verzióból is akad jópár (1.0, 1.1, 2.0, 3.0, 3.5, 4.0, stb), sőt olykor nem is hajlandó valamilyen oknál fogva feltelepülni a gépünkre a szükséges verzió. Ilyenkor jön a fejvakarás, és a Google. Ha az előbbi két megoldás csődöt mondott (minimális esély van rá), akkor elég nagy baj van ahhoz, hogy a következő agresszív beavatkozást végrehajtsuk rendszerünkön.

A küldetés célja: a .NET Framework (2.0, 3.5) telepítése a gépre. Szükséges lépések:
  1. Töltsük le ezt a kis csomagot: NetFX CleanUp Utility
  2. Csomagoljuk ki valahová, majd futtasd a cleanup_tool.exe fájlt.
  3. Válasszuk ki, hogy a .NET összes verzióját törölje le a program minden registry bejegyzéssel együtt.
  4. Ha elkészült (eltarthat egy jó pár percig), akkor indítsuk újra a gépet.
  5. Telepítsük fel a .NET 2.0 SP2 újraterjeszthető csomagot. (előfordulhat, hogy kelleni fog a Windows Installer 3.1 Redistributable (v2) - Magyar verzió is)
  6. Telepítsük fel a Windows Installer 4.5-öt (minimum XP SP2 kell hozzá!)
  7. Telepítsük a .NET 3.5 SP1 újraterjeszthető csomagját.
És ha mindent jól csinálunk, akkor minden rendben lesz a .NET körül.

2010. október 7., csütörtök

Nyílt forrású csomagkezelőt adott ki a Microsoft

A Microsoft szerdán jelentette be NuPack nevű nyílt forrású csomagkezelő rendszere egy előzetes változatának elérhetőségét. A .NET alapú projektekhez használható "package manager" szoftver célja, hogy egyszerűvé tegye a fejlesztők és a felhasználók számára az alkalmazásokban felhasználható nyílt forrású könyvtárak megkeresését, telepítését és beüzemelését a Windows rendszereken.

"A NuPack-kal az a célunk, hogy olyan egyszerűvé tegyük a nyílt forrású könyvtárak integrálását a .NET projektkbe, amennyire csak lehet", közölte Scott Guthrie a csomagkezelőt bemutató blogbejegyzésében, amely szerint egyébként a NuPack-nak a Microsoft jövőben központi szerepet kíván szánni a .NET platformon. Ez utóbbi többek között azt jelenti, hogy az új csomagkezelő natív támogatást fog kapni a Visual Studio fejlesztőrendszerben, amely automatikusan képes lesz az elkészült szoftvereknek megfelelő csomagok legenerálására.

A NuPack csomagkezelő minden Visual Studio változatba be fog kerülni

A NuPack lehetőséget ad a fejlesztők számára, hogy azok alkalmazásaikat egy központi tárházba (repository) regisztrálják, amit más fejlesztők szabadon kereshetnek, illetve a bennük elhelyezett könyvtárakat letölthetik és beépíthetik saját alkalmazásaikba. A csomagkezelő nem a könyvtár részét képező kódokat tölti le, de az által igényelt esetleges függőségeket is, amelyeket természetesen automatikusan kezel a rendszer.

További részletek és dokumentáció itt.

Sting

2010. július 30., péntek

Struktúra vagy osztály?

Azon programozási nyelveknél, amelyeknél létezik struktúra (struct) és osztály (class) típus is, időről-időre talán felvetődik az a kellemes kérdés, hogy vajon adott helyzetben az egyiket, vagy inkább a másikat kellene-e használni? Itt most elsősorban olyan helyzetekre (feladatokra) gondoltam, amelyekhez egyébként bármelyik típus megfelelő lenne (mondjuk azért, mert mindkettőnek lehet adattagja, metódusa, konstruktora, blabla...), de valamilyen oknál fogva nekünk mégis le kell tennünk a voksunkat az egyik mellett. Izgalmas kérdés, vizsgáljuk meg hát a dolgot .NET környezetben.
Sok apró különbség van a struct és class között, de az apróságokat most hagyjuk másra. Az első komolyabb eltérés az, hogy a class öröklődhet (egyik a másikból ugyebár), míg a struct nem. Ez tehát eleve döntő szempont lehet akkor, ha egy hierarchiát, vagy objektum-orientált tulajdonságokkal rendelkező struktúrát kell felépítenünk, mivel így ehhez a feladathoz csakis a class jöhet szóba.
A másik alapvető különbség az, hogy míg a class referencia (reference), addig a struct érték (value) típus. Na, ez már valami! A .NET-ben a referencia típusok a heap-en foglalnak helyet, és a már felesleges példányok eltakarításával a szemétgyűjtő (garbage collector) van megbízva. A szemétgyűjtő nem fut (nem takarít) állandóan, csak akkor, ha úri kedve úgy tartja, vagy inkább... amikor eljött az ideje. Ezzel ellentétesen az érték típusok a stack-en (veremben) tárolódnak, és az élettartamuk addig tart, amíg az adott típus hatóköre tart (pl.: egy függvény elejétől a végéig). Amint megszűnik a hatóköre, azonnal eltűnik a stack-ről, és hmmm... legalábbis nem foglal fizikailag helyet tovább.
Érték típussal általában nem annyira költséges dolgozni, mint referencia típussal egészen addig, amíg becsomagolásra vagy kicsomagolásra nem kerül sor. A becsomagolás (boxing) az, amikor egy érték típust átalakítunk referencia típussá (pl.: egy int belekerül egy object-be), a kicsomagolás (unboxing) pedig ennek a fordítottja. Csomagoláskor tehát az érték típus (vagyis a struct) költségesebb, mint az osztály (vagyis a class), mert - hogy a jelen példára alapozzak - egyszerűen több munka van vele.
Általában elmondható még az is, hogy érdemesebb class-t használni akkor, ha annak a memóriafoglalása (footprint) 16 bájt alatti, illetve a belőle létrehozott példány állapota (tartalma) nem módosul. Szóval akkor most struktúra vagy osztály? A válasz, mint látjuk: attól függ.

Put About

***

Soci jelezte nekem, hogy ez az írásom pontosítást igényel. Örülök, hogy felhívta rá a figyelmemet, és ezennel szeretném egy az egyben közzé tenni az általa írt kiegészítés szövegét: "A value typeok vermen laknak, ha lokális változóként példányosítjuk őket, de a heapen, ha része egy ref típusnak. A lényeg, hogy az őt tároló konténernek közvetlen része, ami verem, ha lokális változó és heap, ha az egy ott lakó ref típus." Socinak pedig még egyszer hálásan köszönöm.