Bejegyzés

Öt Világ – Szoftverfejlesztés dimenziói

Joel Spolsky írása ugyan 2002-es dátumú, de még mindig tartalmaz igazságokat. Lássuk magyarul!

Van egy nagyon fontos dolog, amit egyszer sem említenek a programozásról és szoftverfejlesztésről szóló könyvek, és amiért néha félreértjük egymást. Te szoftverfejlesztő vagy. Én is. Ám nem biztos, hogy ugyanolyan céljaink és követelményeink vannak. Tulajdonképpen a szoftverfejlesztésből több fajta létezik, és mindegyik külön világra más és más szabályok érvényesek.

Ha egy UML-ről szóló könyvet olvasol, sehol sem találod meg benne, hogy az alkalmatlan eszközmeghajtók írásakor. Vagy olvashatsz egy olyan cikket, ami azt írja, hogy „A 20MB-s futtatókörnyezet (a .NET-hez) nem lehet akadály”, de nem mondja ki a nyílvánvalót: ha csak egy 32KB ROM-al rendelkező csipogóba írsz, akkor igenis van jelentősége!

Úgy gondolom, öt különálló világról beszélhetünk, amik néha átfedik egymást, de gyakran nem. Ezek:

  1. dobozos
  2. belső
  3. beépített
  4. játék
  5. egyszer használatos

Ha a legújabb Extrém Programozásról szóló könyvet olvasol, vagy Steve McConnel remek könyveit, vagy akár a Joel on Software-t, esetleg a Software Development magazint, egy rakás olyan kijelentéssel találkozhatsz, hogy így vagy úgy fejlessz szoftvert, de arról nem szól a fáma, miféle fejlesztésről is beszélnek. Ez azért nem szerencsés, mert a különböző világokban lehetséges, hogy másképp kell csinálnod a dolgokat.

Nézzük át röviden a kategóriákat!

Dobozos az a szoftver, amit „a vadonban” használ nagy számú felhasználó. Talán még be is dobozolják, és a CompUSA-ban is árulják, de akár internetről letölthető is lehet. Lehet pénzes vagy shareware, nyílt forrású, GPL-es, akármi – a lényeg az, hogy akár ezrek vagy milliók használják.

A dobozos szoftvernek van néhány problémája, ami két különleges tulajdonságából adódik:

  • mivel olyan sok felhasználó van, akiknek más megoldások is rendelkezésükre áll, a felhasználói felületnek az átlagosnál egyszerűbbnek kell lennie, hogy sikeres lehessen
  • mivel olyan sok számítógépen kell futnia, a kódnak különösen rugalmasnak kell lennie, már ami a különböző konfigurációkat illeti. Múlt héten kaptam egy olyan hibát a CityDesk-re, ami csak lengyel Windows-nál fordul elő, mivel az operációs rendszerben a jobb Alt-ot kell használni a speciális karakterek beírásához. Kipróbáltuk Windows95-ön, 95OSR2-n, 98-an, 98SE-n, Me-n, NT 4.0-n, Win2000-en, és WinXP-n. Teszteltük IE 5.01-en, 5.5-ön, és 6.0-n. Teszteltük amerikai, spanyol, francia, héber, és kínai Windows-szal. Ám még nem foglalkoztunk lengyellel.

A dobozos szoftver három nagyobb fajtája létezik. A nyílt forrású szoftvert leggyakrabban úgy fejlesztik, hogy senki sem kap pénzt a fejlesztésért, ami a dinamikát nagyban befolyásolja. Például azokat a funkciókat, amik nem élvezetesek, nem készítik el a lelkesedésből fejlesztő csapat, és, ahogy Matthew Thomas ékesszólóan rámutat, ez a használhatóságot ronthatja. A fejlesztés valószínűleg nagy földrajzi távolságokat ölel át, ami a csapat kommunikációját radikálisan megváltoztathatja. A nyílt forrású világban elég ritka a személyes megbeszélés, ahol táblára dobozokat és nyilakat rajzolnak. Így a tervezési döntéseket, amik pont a dobozok és nyilak rajzolásából nyernék lényegüket, csak kis hatékonysággal hozzák meg. Végeredményben a földrajzilag szétszórt csapatok sokkal jobbak meglévő szoftverek lemásolásában, ahol elenyésző mennyiségű tervezés szükséges.

A tanácsadószoftver tulajdonképpen csak egy változata a dobozos szoftvernek, amit annyit kell finomhangolni és installálni, hogy egy sereg tanácsadóra van szükség a bevezetéshez, aranyárban. Többnyire a CRM és CMS csomagok esnek ebbe a kategóriába. Az ember könnyen úgy érezheti, hogy ezek valójában nem csinálnak semmit, és ez csak egy indok, hogy tanácsadók tucatjait szabadítsák rá, 300 dolláros órabérrel. Bár a tanácsadószoftver dobozos szoftvernek van álcázva, az implementáció magas költsége alapján inkább belső szoftvernek minősül.

A kereskedelmi webalapú szoftver, mint a Salesforce.com, vagy az eBay változatok még mindig igénylik a könnyű kezelhetőséget, és hogy minden böngészőn fussanak. Bár a fejlesztők megengedhetik maguknak azt a luxust, hogy (legalább egy kicsit) ellenőrizhessék a telepítési környezetet – a számítóközpontban levő gépeket –, kezelniük kell a böngészők széles skáláját, és a felhasználók nagy számát, így ezt én a dobozos szoftver kategóriájába sorolom.

A belső szoftvernek csak egy cég számítógépein kell futnia, ugyanolyan szituációban. Ezt lényegesen egyszerűbb fejleszteni. Számos olyat feltételezhetsz, ami a futási környezettel kapcsolatos. Megkövetelheted az Internet Explorer, a Microsoft Office, vagy akár a Windows egy verzióját. Ha grafikont akarsz csinálni, arra ott lesz az Excel. Nálunk mindenkinek van Excelje. (Ám próbálnád ugyanezt a dobozos szoftverednél, és már el is vesztetted a potenciális vásárlóid felét.)

A használhatóság kevésbé fontos, mivel csak meghatározott számú felhasználónak kell a programot használnia, és ők sem választhatnak másik szoftvert maguknak. A fejlesztési sebesség sokkal fontosabb. Mivel a fejlesztési költségek csak egy cégen belül oszlik el, az elosztható fejlesztési erőforrások is lényegesen kevesebbek. A Microsoft megengedheti magának, hogy ötszáz millió dollárt költsön egy operációs rendszer fejlesztésére, ami egy átlagembernek csak 80 dollárjába kerül. Ám amikor a Detroit Edison energiakereskedelmi szoftvert fejleszt, a befektetésnek ésszerűnek kell lennie az egész társaságnak. Értelmes ROI eléréséhez nem költhetsz annyit, amennyit egy dobozosra költenél. Így sajnos a belső szoftverek nagyon csúnyák.

A beépített szoftverek annyiban egyediek, hogy egy hardverbe kerülnek, és szinte sosem frissíthetők. Ez egy teljesen más világ. A minőségi követelmények itt sokkal magasabbak, mert nincs második lehetőség. Valószínűleg lényegesen lassabb processzorral kell dolgoznod, így sokkal több időt kell szánnod optimalizálásra. A gyorsaság lényegesebb, mint a kód szépsége. Kevesebb be- és kimeneti eszköz állhat rendelkezésre. A múlt héten bérelt autómban a GPS rendszer IO rendszere annyira szánalmas volt, ami szinte teljesen használhatatlanná tette. Próbáltál már ilyen ketyerén beírni egy címet? Megjelenik a képernyőn egy „billentyűzet”, és a nyilakkal kell kiválasztani az öt, egyenként 9 karaktert tartalmazó mátrixból a betűket (a link további illusztrációkat tartamaz a felhasználói felületről. A saját kocsimban levő GPS érintőképernyős, ami a felületet remekül feldobja. Ám elkalandoztam).

A játékok két okból kifolyólag egyediek. Előszöris a játékfejlesztések ökonómiája sikerorientált. Néhány játék sikeres lesz, de sokkal több van bukásra ítélve. Ha pénzt akarsz keresni a játékpiacon, jó, ha ezt felismered, és tartasz néhány sikeres játékot a portfóliódban, hogy a kirobbanó siker nyeresége fedezze a megbukott játékok veszteségeit. Ez inkább a filmszakmához hasonlít, mint szoftvereshez.

A játékfejlesztések nagyobb problémája az, hogy itt is csak egy verziód van. Ha már egy felhasználód végigjátszotta a Duke Nukem 3D-t, nem fog a Duke Nukem 3.1D-re frissíteni csak néhány hiba és pár új fegyver miatt. Néhány kivételtől eltekintve ha valaki már végigjátszott egy játékot, már unni fogja az újra játszást. Így a játékoknak ugyanolyan minőségi követelményeik vannak, mint a beépített szoftvereknek, és hihetetlen nagy pénzügyi szükséglet, hogy elsőre jó legyen. A dobozos szoftverek fejlesztőinek megadatott az a luxus, hogy ha az 1.0-ás verzió nem felelt meg az emberek igényeinek, a 2.0-s talán meg fog.

Végül az egyszer használatos kód az, amit csak azért ácsolsz össze ideiglenesen, hogy valami mást elérj, amit nagy valószínűséggel sosem kell többet használnod, miután azt a valamit elérted. Például írhatsz egy olyan kis shell szkriptet, ami egy bemeneti fájlt úgy masszíroz meg, hogy az olyan formátumba kerül, amit már valami más program már fel tud dolgozni.

Lehetnek még olyan szoftverfajták, amiket kifelejtettem.

Egy fontos dolgot tudnod kell: amikor olyan könyveket olvasol programozási metodológiákról, amiket egy teljes munkaidős szoftverfejlesztő guru / konzulens írt, szinte teljesen biztos, hogy belső céges szoftverfejlesztésről ír. Nem dobozos termékről, nem beépített szoftverről, és biztos, hogy nem játékról. Hogy miért? Mert a cégek bérlik fel ezeket a gurukat. Ők fizetik a számlát (hidd el, az id software nem fogja felvenni Ed Yourdon-t, hogy a struktúrált analízisről beszéljen).

Múlt héten Kent Beck azt állította, hogy valójában nincs is szükség hibakövető adatbázisokra, ha Extrém Programozást használsz, mert a páros programozás (folyamatos kódvizsgálattal) és a teszt-irányított fejlesztés (ami garantálja, hogy az automatikus tesztek 100%-osan lefedik a kódot) azt eredményezi, hogy nem lesz egy hibád sem. Ez valahogy nem tűnt igaznak a szememben. Bele is néztem a saját hibakövető adatbázisunkba, mi is az, ami elfoglal minket.

Na lám csak, azt vettem észre, hogy nagyon kevés hibát vettünk volna észre páros programozással, vagy teszt-irányított fejlesztéssel. A legtöbb „hibánk”, amit XP-ben történetnek hívnak tulajdonképpen fejlesztési kérések. A hibakövető rendszert egyszerűen arra használjuk, hogy ne felejtsük el, priorizáljuk, és menedzseljük azokat az apró csiszolásokat és nagy funkciókat, amiket ki akarunk fejleszteni.

Sok más hiba pedig csak akkor került a felszínre, amikor már a „mezőkön” régóta használták. A lengyel billentyűzet dolog. Ezt páros programozással lehetetlen észrevenni. Aztán azok a logikai tévedések, amik nálunk sosem jöttek elő úgy, ahogy a különböző funkciók együttműködnek. Minél nagyobb és bonyolultabb egy program, annál több a párbeszéd olyan funkciók között, amire nem is gondolsz. Egy bizonyos nem várt karaktersorozat (ha tudni akarod, a „{${?” az) összezavarja a lexert. Néhány ftp kiszolgáló hibát generál, ha egy olyan fájlt akarsz törölni, ami nem is létezik (a mi ftp kiszolgálónk nem kiabál, így ez elő sem fordult nálunk).

Figyelmesen végigtanulmányoztam az összes hibát. 106 hiba közül, amit a CityDesk szervízcsomag kiadásában megjavítottunk, pontosan ötöt tudtunk volna páros programozással, vagy teszt-vezérelt tervezéssel kivédeni. Sokkal több hibánk volt olyan, amikről tudtunk, amikről úgy gondoltuk, nem fontosak (kivéve persze a vásárlóinkat!), mint olyanok, amiket az XP módszerrel kivédhettünk volna.

Ám Kent-nek igaza van a másik fajta fejlesztésben. A legtöbb céges fejlesztésű alkalmazásnál ezek egyike sem lett volna hiba. A program lefagy érvénytelen bevitelkor? Futtasd újra, de ezúttal figyelj a {${?-kre! Aztán úgyis csak egy fajta ftp kiszolgáló lesz, és a cégben senki sem fog lengyel Windows-t használni.

A szoftverfejlesztés legnagyobb része ugyanaz, akármiféle projekten is dolgozol, de nem teljesen. Ha valaki metodológiáról beszél, gondolkodj el azon, hogy ez hogy hatna a te munkádra. Gondolkodj el azon, hogy milyen környezetben is van a másik. Steve McConnell, Steve Maguire, és jómagam egy nagyon szűk sarokból jövünk: a dobozos tömegcikk táblázatkezelő alkalmazások világából, amit Redmondban, Washington államban írtak. Nálunk magasabb a léc az egyszerű használatnál, és alacsonyabb a hibáknál. Az összes többi módszertan guru azzal keresi a betevőt, hogy konzultál saját céges fejlesztéseknél, és ők erről is beszélnek. Bárhogyis, mindannyian tudnunk kellene tanulni egymástól.

Forrás: http://js.hu/jos/

HashList – Dictionary többszörös kulccsal

A pascal-os időszámításhoz képest a modern framework-ökben számos segédeszköz rendelkezésre áll, sok adattároló szerkezetet használhatunk, sőt a nagy ugrás, a generikus adattípusokra az ember úgy gondol, mint a netovábbra, a végtelenre. De volt, amit nem találtunk meg…

KeyValue párokat szerettünk volna tárolni, Key szerint keresni, Value-kat kinyerni. Jött az ötlet Dictionary<Key, Value>. Specifikáció szerint viszont ennek egy Key-je egyszer szerepelhet a gyűjteményben, minden további értékeadás felülírja az előzőt. Azaz a Key-eknek egyedinek kell lenniük. Pár éve már sejtettük ezt.

Nekünk viszont irányítószámokat kell tárolnunk és hála a magyar közigazgatásnak nem minden település kap saját számot, előfordul, hogy egynél több település fut ugyanolyan “kód” (irányítószám) alatt. Az ötlet, mely szerint Dictionary<int, List<string>> adatszerkezetbe rakjuk az információkat, jónak tűnt, de hátha ezt már valaki kidolgozta.

És ekkor találtuk meg többedszerre az NGenerics családot, amely számos, C#-ból kimaradt, bonyolultságuk és sokrétűségük miatt speciális tudást igénylő generikus típussal engedi bővíteni a készletet. Ennek HashList osztályát használtuk, amely az alábbi nyelvi konstrukcióban engedte feltölteni ZIP (IRSZ) törzsünket.

 

citylist.Add(2066, "Szár");
citylist.Add(2066, "Újbarok");

 

Az osztálygyűjtemény nagyon nagy. Szerettük volna kerülni az irányítószám kezelés miatt akár 30-40 másodperccel is megnövekedett fordítási időt. Ezért csak bizonyos részeket használtunk fel belőle. Matematikusok tervezték, tele van interfésszel, látogató termintával és minden szépséggel, emiatt az általunk használt osztályhoz a következő fájlok mindegyikére szükség volt:

 

HashList.cs
IVisitable.cs
IVisitableCollection.cs
IVisitableDictionary.cs
IVisitor.cs
VisitableHashtable.cs

 

Projektünk sikeresen kiadásra került, a projektben résztvevő személyek mindegyike sikeresnek értékelte a megoldást. Köszönjük NGenerics!

Kivételek típusai – melyiket dobjam?

A kivételkezelés alatt sok fejlesztő a catch ág megvalósítását gondolja, de ugyanolyan fontos a kivételek eldobása is. Nem szabad azzal megelégedni, hogy dobunk egy ApplicationException-t, sokkal precízebb, ha a típusos esetekben (rossz paraméter, nem jó képformátum) a beépített kivételosztályokat használjuk.

Lássuk, mik ezek:

Kivétel osztály Kiváltás oka
SystemException Futásidejű hiba, a kivételes ősosztálya
AccessException Egy típus elemeléréseinek hibája (metódus, mező, property)
ArgumentException Metódushívás esetén hibás paraméter
ArgumentNullException Metódushívás esetén null paraméter, ha azt a metódus nem tudja kezelni
ArgumentOutOfRangeException Paraméter értéke adott határokon kívül esik
ArithmeticException “Matematikai” hiba
ArrayTypeMismatchException Típusos tömbön végzett művelet egy idegen típussal
BadImageFormatException Rossz képformátum
CoreException Futásidejű kivételes ősosztálya
DivideByZeroException Nullával való osztás
FormatException Argumentum formátuma nem helyes (pl: String.Format)
IndexOutOfRangeException Tömb indexelése túlmutat a határokon
InvalidCastExpression Futásidejű Cast művelet nem hajtható végre
InvalidOperationException Nem megfelelő (idejű?) művelet hívása
MissingMemberException DLL verziószám ütközés, eltérés metódushívás közben
NotFiniteNumberException Nem valós szám (decimal, float; NaN, Infinity)
NotSupportedException Nem létező metódus hívása (reflection?)
NullReferenceException NULL értékű változó által hivatkozott objektum elérése
OutOfMemoryException Memória elfogyás
StackOverflowException Verem műveletek memória elfogyása (rekurízió)

A fenti lista számos lehetőséget kínál a fejlesztőknek a megfelelő kivétel eldobásában. Ezek használata nagyban megkönnyíti a hibakezelést és hibakeresést, a metódus írója pedig publikálhatja, hogy mit várt és mit kapott.

Diagram engine – miért C#

Ügyviteli rendszereknél ritka, hogy diagramot jelenítünk meg valamit, hiszen minden adat táblákban van, azok megjelenítéséhez pedig ideális valamiféle grid. Néha szoktak beépített grafikon segítségével felhasználókat elképráztatni, de az egyedi rajzolásos digram nem mindennapos.

Az üzletkötőink, ügyviteli szakembereink sem értették, hogy mit akarunk ezzel, miért nem jó szerintünk a sima kis lista. Csak a végén értették meg, hogy mit is akartunk.

Ezt:

voucherdiagram

Hogyan is jött létre ez a – joggal innovációnak nevezhető – modul, amely minden ügyviteli termékünkben megjelenik és sok pozitív felhasználói visszajelzést indukált? A bizonylatok és egyéb adatelemek közti reláció, adatkapcsolat adott, erre épül maga az alkalmazás. No de ezt hogy jelenítsük meg egy gráfban, hogy  felhasználó is kedvet kapjon és használja?

Csapatunk 3 napon keresztül tervezett, brainstormingolt. Számos ötlet jelent meg a fejekben és a nagy prezentációs falon, ezen ötletek kb. 50% bele is került a megvalósult rendszerbe. Az elvárások tisztázása után kezdődött a fejlesztés. Programnyelv C#, ennek GDI és GDI+ lehetőségei kiváló kiaknázásra való területet jelentettek számunkra.

Lépések:

  • Építsünk egy saját controlt.
  • Bármilyen megjelenni kívánó objektum feleljen megy egy IDiagramDrawItem interfésznek, amely egy metódust definiál void Draw() és információt szolgáltat arról, hogy melyik rétegben jelenik meg a kirajzolandó adatelem int ZOrder { get; }.
  • Rajzolás megvalósítása
    • override Paint()
    • void ClearAndDrawBackground()
    • void SortByZOrder()
    • foreach(... item.Draw()...)

Már a tervezési fázisban is láthatóvá vált, hogy lesznek optimalizálási kérdések, problémák, amelyek a témakör izgalmasabb részét jelentik.

Repository, hogy ne szaggasson a kép. Egyedileg rajzoló eljárások rákfenéje a sok GDI objektum, amely memóriában és időben is költséges. A memóriabeli költségeket, mivel IDisposasble, meg lehet oldani, de sokáig tart minden OnPaintben Font-os, Pen-t és Brush-t létrehozni. Erre született megoldásként, hogy a diagram ezeket publikálja, tárolja, elérhetővé teszi eg példányban, egy alkalommal való létrehozással a rajzolni képes objektumok felé. Performancia mérést végeztünk, egy átlagos diagram 740 alkalommal tud kirajzolódni egy másodperc alatt. Nem rossz, megfelel.

GraphicsPath, hogy lekerekített sarkú legyen a doboz. A C# GDI+ lehetőséget kínál arra, hogy vonalak és görbék segítségével egy “utat” hozzunk létre, amely felhasználható rajzolásra és kitöltésre egyaránt. Megvalósítottuk. Mivel ez is költséges, minden objektum a mérete alapján (amely nem állítható megjelenítés közben) első felhasználáskor (late-init) létrehozza a GraphicsPath objektumot.

LinearGradientBrush, hogy átmenetes legyen a dobozok háttere. A legnagyobb kihívás a színátmenetes doboz volt, amely egy pontoktól függő GradientBrush lett. Ennek szintén elég egy példányban léteznie, de a doboz pozíciójának függvényében kell felparaméterezni. A (rendszer által) mozgatható dobozok pedig ezen tulajdonságukat gyakran változtatják, de erre is született megoldás: late-init és re-init-by-move.

És ekkor még nem ért véget a gondolkodás, a dobozok esztétikus elhelyezésének algoritmusa felér egy komolyabb diplomamunka témakörével, erről egy következő cikkben.