Hiba elrejtése
A hiba elrejtése a számítógép-programozás antimintája. A programozó a kivételkezelés segítségével elrejti a hibákat. Ennek eredményeként a hibaüzenet rejtve marad a felhasználó előtt, és nem tájékoztatja arról, hogy milyen hiba történt. Célja az lehet, hogy azt a benyomást keltse, hogy a program sosem hibázik, azonban nem ment meg a hibáktól, mivel azok gyakran külső körülményeken múlnak.
Talán a programozó fél a hibáktól, vagy attól tart, hogy a felhasználó számára túl bonyolult, érthetetlen hibaüzenet jelenik meg. Azonban nemcsak a felhasználók futtatják a programot, hanem karbantartást, hibakeresést végző programozók is, akik megpróbálják megtalálni, mi a hiba. Az eredeti programozót nem azért fogják szidni, mert a program néha hibázik, hanem azért, mert úgy elrejtette a hibát, hogy eszközeikkel csak nehezen, vagy egyáltalán nem képesek megtalálni, és kézi módszerrel kell átfésülni a kódot, és vissza kell térni a primitív módszerekhez, kódszakaszok kiiktatásához és gyakori kiíráshoz. Ha a hibaüzenet érthetetlen lenne a felhasználónak, akkor naplózást lehet használni, ami külön fájlba menti el az üzenetet.
A hiba elrejtésének egy harmadik oka, hogy igazából nincs is hiba, a program képes együtt élni azzal az állapottal, amit a hiba jelez. Ez néha igaz is, de nem mindig. Érdemes ezt az állapotot is naplózni, hátha tévedett a programozó, és mégiscsak hiba.
Példák
[szerkesztés]Egy egyszerű példa a következő:
Example:
try ImportFile(filename); except // a kivétel üzenete alig tartalmaz információt raise Exception.Create('import failed'); end;
Ez a kódszakasz egy fájlt nyit meg, és olvas a memóriába. Ha bármi miatt hibázik, akkor csak azt tudatja, hogy az import nem sikerült, még csak azt sem tudjuk meg, hogy melyik fájlról van szó, vagy hogy miért.
Jobb megközelítés:
try ImportFile(filename); except on E:Exception do begin // informatív üzenet E.Message := 'Import of file <'+filename+'> failed.'+#13#10 + E.Message; // a kivétel újradobása raise; end; end;
Ebből az üzenetből legalább kiderül, hogy melyik fájllal volt probléma, ezzel már el lehet indulni az okokat keresni. Egy fejlettebb üzenet még több információt hordoz, leírja az import bukásának okát:
- A fájl nem létezik. (File does not exist)
- A fájl sérült. (File appears to be damaged)
- A fájl nem férhető hozzá. (Do not have permission to access this file)
- …
- és a hibákat külön log fájlba írja. Nagyobb alkalmazások külön trace fájlokat generálhatnak, amelyek részletesen feljegyzik a rendszer állapotát a hiba előfordulásakor.
Néha a rendszer hajlamos elrejteni a hibát. Például a Java 1.4 verziója előtt egy nem létező fájl megnyitása IOException hibaüzenetet eredményezett, ami nem hordozta a szükséges információkat. Az ilyen kivételeket el kell kapni, és dobni egy másik, informatívabb kivételt, ami tartalmazza a rendszer szempontjából fontos információkat.
A hiba tagadásának módja, hogy vagy üresen hagyják a catch részt, vagy az állapottól függő kódot hajtatnak végre. Az okozó állapotot sosem látják a felhasználók és naplózás híján a tesztelők sem. Néha valóban jó döntés a hiba tagadása, ha például a kivétel azt jelzi, hogy a fájl véget ért, ekkor az olvasást be kell fejezni és a fájlt be kell zárni, a beolvasott szöveget pedig további feldolgozásra átadni.
C++ példa:
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cerrno>
using namespace std;
int main(int argc, char **argv)
{
string result;
try {
ifstream input_file("nonexistent.txt", ios::in);
if(!input_file) throw strerror(errno);
input_file >> result;
cout << result;
}
catch(...) {
// Error messages are for wussies
}
cout << "Result is " << result << endl;
return 0;
}
A programozó tévedése esetén ezt a kódot nehéz debuggolni, különösen beágyazott üres try-catch blokkok esetén. Bármely anomáliát nehéz visszanyomozni, ami megnehezíti a karbantartást, csak hogy fenntartsák a robosztusság látszatát.
VBScript példa:
If doSomething() Then
If doSomethingElse() Then
If anotherDoSomething() Then
anotherOtherDoSomething()
End If
End If
End If
Ekkor a kód minden hibát elrejt, mivel egy Else további hibát válthat ki, vagy nem elég tömör. Ha If/End If szakaszok nélkül írnák a kódot, akkor ha valahol van valami, akkor a végrehajtás elakad, de senki sem fogja tudni, hogy miért, hol van a hiba, amit elrejtettek. Felbukkanhat véletlenül, akár több hónappal at eredeti hiba bevezetése előtt.
Ellenőrzött kivételeket támogató nyelvekben
[szerkesztés]Az ellenőrzött kivételeket támogató nyelvekben, mint például a Java, a hiba elrejtése a leggyakoribb antiminta. Ez arra kényszeríti a programozót, hogy kezelje a kivételeket, még akkor is, ha nem akarná, vagy nem tudná jól elvégezni. Nagy vagy bonyolult rendszerekben ez súlyosabbá válhat. Például A, B és C komponensek, A delegál B-nek, és B delegál C-nek. Ha hiba adódik, akkor C ellenőrzött kivételt dob. B foglalkozik a kivétellel, de nem tudja kiszolgálni A-t, így továbbdobja a kivételt. Ekkor A-nak három lehetősége van:
- A kivétel továbbdobása
- Elkapja a kivételt, naplózza és újra eldobja
- Elkapja a kivételt, és nem csinál vele semmi értelmeset.
Egy újonc választhatja a harmadik lehetőséget. A kivétel lenyelésével a rendszer bizonytalanná válik, így a felhasználók és kliensek nem vesznek észre semmit, pedig lehet, hogy a hiba kritikus. Ha egyszer mégiscsak észreveszik, akkor a debuggoló eszközök alig vagy egyáltalán nem használhatók, hiszen a hiba jóval távolabb keletkezett, mint ahol felbukkant.
Egy nagy, több rétegű alkalmazás, amiben sok objektum kommunikál, az ellenőrzött kivételnek számos rétegen kell áthaladnia, mielőtt elér egy olyan pontra, ahol értelmesen tudják kezelni, vagy közölni a felhasználóval. Azzal, hogy bármely rétegben elrejthetik, a tartománymodellek beszennyeződnek a fölösleges másolással.
Emiatt a Java 1.4 bevezette a kivételek automatikus továbbítását.
Fordítás
[szerkesztés]Ez a szócikk részben vagy egészben az Error hiding című angol Wikipédia-szócikk fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.