Kai Delphi biblioteka įgyja statymo konfigūraciją be vaizdinės sistemos, pakaitinės klasės yra ta vieta, kur gyvena klaidos. Ne platforma, ne kompiliatorius: pakaitalai (stand-in). PDFlibPas turi grafikos sluoksnį, teikiantį bitmap, drobės, šriftų, metafailo ir spausdintuvo atitikmenis versijoms be VCL, ir perkėlimas į Free Pascal iškėlė į dieną kiekvieną gedimo formą, kokią pakaitalas gali turėti. Jos tvarkingai rūšiuojasi pagal diagnostikos kainą, o ta tvarka yra priešinga tai, ką numano intuicija
Pakaitalas, metantis išimtį, randamas pigiai; išimtis įvardija metodą. Pakaitalas, grąžinantis tuščius duomenis, yra brangus, nes gedimas pasirodo keliais sluoksniais nuo savo priežasties. Pakaitalas, grąžinantis sėkmę, yra blogiausias iš visų, nes grąžos kodas teisėtas, klaidos kodas nulinis, išimtis nepakyla, ir vienintelis įrodymas, kad kažkas ėjo negerai, yra išėjusiuose baituose
Trečia forma: teisėtas atvaizdo identifikatorius ant tuščio XObject
Vektorinis metafailo konverteris nebūsiančioje VCL konfigūracijoje buvo tuščias procedūros kūnas. Viskas virš jo toliau dirbo. EMF importo įėjimo taškai ir drobės pagaunimo įėjimo taškas pasiekdavo pabaigą ir grąžindavo teisėtą atvaizdo identifikatorių, kurį iškviečiantysis tada padėdavo ant puslapio. Faile atsidurdavo form XObject su turinio ilgiu nulis. Puslapis atvaizduodavosi baltas
Niekas nepranešė problemos, ir tai apima pačios bibliotekos demonstracinę šios savybės programą, kuri nupiešė tuščią puslapį ir to nepastebėjo. Nebuvo nesėkmingos grąžos reikšmės, kurią tikrinti, nes iškvietimų seka iš tikrųjų visa sėkminga; vienintelis neteisingas dalykas buvo pagaminto srauto dydis. Tokios klasės defekto diagnostika reiškia kitokio klausimo uždavimą: ne „ar iškvietimas nepavyko“, o „ar artefaktas tikėtinas“. Form XObject ilgio nulis, atvaizdas iš nulio pikselių, puslapis iš nulio turinio baitų — tai teiginiai, kurie tai pagauna
Pataisa turi dvi puses, ir antroji lengvai užmirštama. Pirma, padarykite, kad tuščia realizacija mestų išimtį, kad gedimas apskritai turėtų kanalą. Antra, tą išimtį paversti nuliniu rezultatu atvaizdų fabrike ir pridėti nulio patikras dviejose vietose, kurios vartoja atvaizdo identifikatorių, nes kitaip „švarus gedimas“ virsta tiesiogine prieigos pažeidimu, kai puslapių medis išreferuoja nieką. Išimtį metanti iškarpa yra patobulinimas tik tada, jeigu iškviečiantieji buvo pasiruošę gedimui, kurio anksčiau apskritai negalėjo gauti
Antra forma: tušti duomenys, trys sluoksniai nuo strigties
Metafailo drobės pakaitalas neužpildė savo fizinių matmenų. Ta reikšmė dalinama į puslapio geometrijos skaičiavimą, todėl skaičiavimas davė nulį, todėl ribinio langelio skaičiavimas dalino iš nulio. Plikas išimčių apdorotojas tai prarijo, atvaizdų fabrikas grąžino nulinį rezultatą, o prieigos pažeidimas galiausiai įvyko puslapių medyje, kai nulis panaudotas. Trys sluoksniai tarp priežasties ir simptomo, o viduryje — išimčių apdorotojas, ištrinantis įrodymus
Tas pats modulis turėjo dar du to šablono atvejus. Šriftų klasė turėjo tuščius Assign ir konstruktoriaus kūnus, kas svarbiau, nei atrodo, nes drobės šriftų savybė yra tik skaitoma: priskyrimas į ją yra vienintelis būdas pristatyti šriftą, todėl tuščia realizacija daro šriftų pasirinkimą tyloje neveiksmingą, o tekstas išeina toks, koks buvo numatytasis. Ir pikselių colyje reikšmė nulis padarydavo, kad kiekvienas iškviečiantysis, drobės dydį nustatantis pagal šriftų metriką, pagamindavo nulio iš nulio drobę, kas duoda tuščią puslapį ir sėkmės grąžą
// Forma, kurios ieškoti pakaitinio modulio viduje: metodas, kuris
// nei meta išimties, nei nieko nedaro. Abu šie kompiliuojasi ir abu
// duoda „sėkmę“ be jokios išvesties
procedure TMetafileCanvasStandIn.Create(...);
begin
// jokio inherited iškvietimo, jokios laukų inicializacijos
end;
function TBitmapStandIn.LoadFromStream(Stream: TStream): Boolean;
begin
Result := True; // o bitmap vis dar tuščias
end;
Placioji struktūra, laikanti tik pirmąjį simbolį
Šis apskritai nėra pakaitalų problema, bet jis priklauso tai pačiai katalogui, nes simptomas lygiai taip pat toli nuo priežasties. Spausdintuvų išvardinimo struktūra buvo deklaruota su visais dvylika jos eilutės narių, paskelbtais rodyklėmis į vieno baito simbolius, o ją užpildanti funkcija yra plačiųjų simbolių išvardinimo API variantas
Rodyklių dydžiai identiški, todėl struktūros išdėstymas teisingas ir niekas nesugriūna. Vietoj to vyksta tai, kad UTF-16 eilutės skaitymas kaip vieno baito eilutės sustoja ties pirmuoju nuliniu baitu, kuris bet kokiam ASCII spausdintuvo vardui yra antrojo simbolio aukštoji pusė. Kiekvienas spausdintuvo vardas grįždavo lygiai vienu simboliu. Toliau vardo patikra nepavykdavo, spausdintuvo sukūrimas nepavykdavo ir spausdinimas nepavykdavo kiekvienam tikram mašinos spausdintuvui, ir nė vienas tų simptomų nerodo į struktūros deklaraciją
// Neteisinga: teisingas dydis, neteisingas elementų tipas. Jokios
// kompiliavimo klaidos, jokio strigtimo, kiekviena eilutė nukirpta
// iki vieno simbolio
type
TPrinterInfo2Wrong = record
pServerName: PAnsiChar;
pPrinterName: PAnsiChar;
// ... dar dešimt
end;
// Teisinga: *W struktūra turi plačiuosius narius nuo galo iki galo
type
TPrinterInfo2W = record
pServerName: PWideChar;
pPrinterName: PWideChar;
// ... dar dešimt
end;
Iš to kylanti taisyklė mechaninė ir verta taikyti nesvarstant: kiekvienai Win32 struktūrai, kurios vardas baigiasi W, patikrinkite, kad kiekvienas eilutės narys būtų platioji variantas, laukas po lauko. ANSI ir plačiųjų pasaulių maišymas neduoda nei kompiliatoriaus diagnostikos, nei strigties — tik tylų nukirpimą, ir tas pats atvirkščiai taikoma ANSI variantams
Plikas išimčių apdorotojas yra tikrasis priešininkas
Kiekvienas šių tyrimų buvo sulėtintas tos pačios konstrukcijos: apdorotojo, kuris sugauna viską ir paverčia neteisinga grąžos reikšme. Tai pagrįstas dalykas rašyti aplink atvaizdų dekoderį, nes sugadintas atvaizdas neturi nukelti dokumento darbo. Tai taip pat prietaisas ištrinti tą vienintelę informacijos detalę, kurios jums reikia
Praktinis atsakas — padaryti apdorojusįjį laikinai triukšmingą. Išimties klasės, pranešimo ir atgalinio pėdsako išmetimas iš vidaus pliko apdorotojo, po debug sąlyga, nepaaiškinamą nulinę grąžą paverčia įvardyta išimtimi su vieta. Dviem iš trijų aukščiau esančių atvejų tas vienas žingsnis užbaigė tyrimą, nes išimtis buvo dalyba iš nulio arba prieigos pažeidimas pakaitinio metodo, kurio vardas pasakė viską, viduje
Kontrolinis sąrašas priimant pakaitinį kelią
Keturios dalys, pagal tai, kokia tvarka jos atsiperka. Prieš iškviesdami pakaitinę klasę, perskaitykite metodus, kuriuos ketinate naudoti, ir patvirtinkite, kad kiekvienas turi tikrą kūną; tuščias kūnas nėra įgyvendinimo detalė — tai dingusi savybė. Teikite pirmenybę pakaitalams, metantiems išimtį, o ne tiems, kurie grąžina neutralias reikšmes, ir suporuokite tai su nulio patikromis tose vietose, kur fabrikas dabar teisėtai gali negrąžinti nieko. Savybę tikrinkite tyrinėdami artefaktą, o ne grąžos kodą, nes visas čia esantis gedimo režimas yra švarus grąžos kodas ant tuščio artefakto; baitų lygio išskaidymas, ką dokumentas iš tikrųjų turi, yra greičiausias kelias tai pamatyti, ir failo dydžio audito straipsnis dengia tą įrankių komplektą. Ir kai savybė neturi gyvybingo pakaitinio įgyvendinimo, nukreipkite paveiktus pavyzdžius į veikiantį kelią ir komentaruose pasakykite kodėl, o ne palikite demonstraciją, kuri tyloje gamina tuščią išvestį
Platesnė išvada taikoma gerokai už vienos bibliotekos ribų. Bet kuri kodų bazė su sąlyginiu antru įgyvendinimu, maketo (mock) sluoksniu, headless režimu, platformos pleistru yra veikiama trečios formos. Kodėl ji taip gerai slepiasi — todėl, kad kiekvienos kokybės vartai, kurių komanda įprastai laikosi, grąžos kodai, klaidų kodai, išimtys, išėjimo būsenos, yra būsenos kanalas, o trečia forma visus juos laiko švarius. Tik išvestis jį išduoda. Tai irgi argumentas, kodėl tvarkant nepatikimą įvestį tikrinami artefaktai, o ne būsenos — aprašyta nepatikimo PDF analizavimo straipsnyje, ir kodėl atvaizduota išvestis lyginama tarp variklių, o ne pasitikima vienu — aprašyta kelių variklių atvaizdavime
PDFlibPas yra savoji Object Pascal PDF biblioteka Delphi, C++Builder ir Free Pascal aplinkoms, o jos nebūsianti VCL konfigūracija yra tai, kas padaro headless ir tarpįrankinius statymus įmanomus; esamas konfigūracijų dengimas išvardytas losLab PDF Developer Library produkto puslapyje