High Order Messaging (dßle jen HOM) je objektovß technologie, kterß umo₧nφ podstatn²m zp∙sobem zjednoduÜit k≤d a zv²Üit jeho flexibilitu. P°ed Φasem jsme se seznßmili s jejφmi zßklady (viz Chip 12/02 nebo www.ocs.cz/Text/HOM.html). Dnes se seznßmφme se zßkladnφmi principy, na nich₧ je technologie HOM zalo₧ena.
HOM bychom mohli voln∞ p°elo₧it jako "nep°φmΘ zasφlßnφ zprßv". P°i objektovΘm programovßnφ objektu b∞₧n∞ posφlßme zprßvy, jejich₧ prost°ednictvφm si vy₧ßdßme n∞jakΘ akce (Java vyu₧φvß jinΘ terminologie - mφsto zasφlßnφ zprßv hovo°φ o volßnφ metod; my vÜak budeme vyu₧φvat standardnφ objektovΘ terminologie a budeme mluvit o zprßvßch).
Z┴KLADY
Princip HOM spoΦφvß v tom, ₧e zprßvu nezasφlßme p°φmo. Mφsto toho "mezi objekt a zprßvu" vlo₧φme dalÜφ p°φkaz, kter² urΦφ, co se se zprßvou mß stßt. Jednφm z nejjednoduÜÜφch p°φklad∙ m∙₧e b²t opo₧d∞nΘ odeslßnφ zprßvy: nap°φklad chceme, aby se okno zav°elo za deset sekund. ╪eÜenφm HOM je pou₧φt specißlnφ "high-order" zprßvu, je₧ objektu °ekne: "Nßsledujφcφ zprßvu mßÜ dostat za deset sekund." Zßpis v Objective C by vypadal takto:
[[okno afterDelay:10] close];
Mo₧nostφ vyu₧itφ slu₧eb HOM je °ada. Velmi vhodnΘ jsou pro prßci s poli, kde uÜet°φ spoustu p°φkaz∙ for: staΦφ zavΘst HOM zprßvu each, kterß poli °ekne: "Nßsledujφcφ zprßvu p°edej ka₧dΘmu ze sv²ch prvk∙." Takto t°eba zav°eme vÜechna okna v poli:
[[poleOken each] close];
A takto ulo₧φme do samostatnΘho pole jejich titulky nebo vybereme jen ta okna, je₧ jsou prßv∞ viditelnß:
Principem funkce HOM je vyu₧itφ tzv. zßstupnΘho objektu. Vra¥me se k p°φkladu se zavφrßnφm oken. Zajistφme, aby libovoln² objekt po p°ijetφ zprßvy afterDelay: vytvo°il a vrßtil nov² objekt t°φdy °ekn∞me DelayedPerformer. Ten bude obsahovat pouze odkaz na p∙vodnφho p°φjemce zprßvy a v naÜem p°φpad∞ takΘ Φasov² interval, urΦujφcφ dobu, na kterou se mß zpracovßnφ zprßvy odlo₧it. Objekt t°φdy Delayed Performer p°φmo nezpracovßvß ₧ßdnou zprßvu. Mφsto toho ve chvφli, kdy jakoukoli zprßvu dostane, ud∞lß dv∞ v∞ci (viz obrßzek):
zprßvu i jejφ p°φpadnΘ argumenty si zapamatuje (nynφ tedy objekt obsahuje trojici ·daj∙: p∙vodnφho p°φjemce zprßvy, Φasov² interval a zprßvu);
spustφ ΦasovaΦ (s d°φve ulo₧en²m Φasov²m intervalem), po jeho₧ uplynutφ p°edß ulo₧enΘmu objektu ulo₧enou zprßvu. U polφ zprßva each vytvo°φ a vrßtφ nov² zßstupn² objekt, t°eba t°φdy Trampoline, kter² obsahuje jen odkaz na pole, je₧ bylo p∙vodnφm p°φjemcem zprßvy. Jakmile objekt t°φdy Trampoline dostane n∞jakou zprßvu, rozeÜle ji vÜem objekt∙m z pole, na n∞₧ mß v sob∞ odkaz.
Slo₧it∞jÜφ je to u zprßv, je₧ vracejφ n∞jakou hodnotu - collect, select apod. Zprßva collect nejen₧e rozeÜle zprßvu vÜem objekt∙m z pole, ale navφc v²sledek ka₧dΘho odeslßnφ ulo₧φ do novΘho pole, je₧ pak vrßtφ. Zprßva select prozkoumß v²sledek odeslßnφ zprßvy a do novΘho pole ulo₧φ ty z p∙vodnφch objekt∙, pro n∞₧ zprßva vrßtila pravdivou Φi nenulovou hodnotu. Obdobn∞ lze konstruovat i dalÜφ, slo₧it∞jÜφ HOM zprßvy.
Ani °et∞zenφ zprßv nenφ ₧ßdn² technick² problΘm: nejjednoduÜÜφ je, kdy₧ zßstupnΘ objekty mφsto jednΘ zprßvy (ji₧ si pamatujφ pro odeslßnφ cφlovΘmu objektu) majφ pro zprßvy celΘ pole. Do n∞j se zprßvy uklßdajφ a p°i vlastnφm zpracovßnφ se odeÜlou vÜechny "najednou" (prvnφ zprßva z pole se poÜle cφlovΘmu objektu, v²sledku se ihned poÜle zprßva druhß, v²sledku se poÜle t°etφ atd.).
Potom je jednoduchß i implementace specißlnφch zprßv begin a end, je₧ umo₧≥ujφ °et∞zenφ zprßv (i t∞ch, je₧ s HOM nemajφ nic spoleΦnΘho). Zprßva begin v zßstupnΘm objektu zvedne pomocn² ΦφtaΦ, zprßva end jej op∞t snφ₧φ. Zßstupn² objekt vyu₧φvß ΦφtaΦ p°i rozhodovßnφ, zda zprßvy uklßdat, nebo rovnou zpracovat: jde-li o HOM zprßvu nebo je-li ΦφtaΦ nenulov², zprßva se ulo₧φ.
Implementace slu₧eb popsan²ch v minulΘm textu o slu₧bßch HOM u₧ by m∞la b²t vcelku z°ejmß: t°eba zprßva safely prost∞ vytvo°φ zßstupn² objekt, kter² svΘmu cφlovΘmu objektu poÜle zprßvu v rßmci zabezpeΦenΘho v²jimkovΘho bloku (try... pro Javu, NS_DURING... NS_HANDLER pro Objective C). Zprßva inNewThread vytvo°φ zßstupn² objekt, kter² svΘmu cφlovΘmu objektu poÜle zprßvu v samostatnΘm threadu.
Specißlnφ technika vÜak je zapot°ebφ pro implementaci automatickΘ synchronizace mezi thready na zßklad∞ vrßcenΘ hodnoty. P°ipome≥me si, o co jde: slu₧ba, je₧ spouÜtφ nov² thread, m∙₧e vrßtit tzv. zßstupn² objekt fault. Ten pouze udr₧uje vazbu na thread a Φekß, a₧ mu kdokoli poÜle jakoukoli zprßvu. Potom zßstupn² objekt vyvolß slu₧bu wait (poΦkß, a₧ thread skonΦφ), nahradφ sßm sebe skuteΦn²m v²sledkem threadu a nakonec jeÜt∞ tomuto v²sledku poÜle zprßvu, ji₧ sßm dostal. V programovacφm jazyce s vyu₧itφm HOM je to jednoduchΘ:
Slu₧ba future spustφ nov² thread a v n∞m p°edß objektu database zprßvu complicated Search stejn∞, jako by to ud∞lala zprßva inNewThread. Zprßva future vÜak navφc vytvo°φ specißlnφ zßstupn² HOM objekt °ekn∞me t°φdy ArrayFault a ten vrßtφ mφsto po₧adovanΘho pole. Objekt ArrayFault obsahuje odkaz na thread, v n∞m₧ probφhß zpracovßnφ; thread zase obsahuje odkaz na ArrayFault. Jestli₧e objekt ArrayFault dostane libovolnou zprßvu, zapamatuje si ji a vyu₧ije systΘmov²ch slu₧eb pro °φzenφ thread∙ k tomu, aby poΦkal na ukonΦenφ threadu, na n∞j₧ je vßzßn. Pak zprßvu poÜle sßm sob∞. (Znφ to divn∞? ╚t∞te dßl!)
Jakmile thread skonΦφ, poslednφ instrukce threadu ulo₧φ v²sledek do pam∞ti na mφsto objektu ArrayFault (objekt ArrayFault zanikne, nebo¥ jeho data jsou p°epsßna daty objektu, jen₧ je v²sledkem prßce threadu). To znamenß, ₧e pokud zßstupn² objekt ArrayFault poslal zprßvu "sßm sob∞" (tj. na svou vlastnφ adresu), ve skuteΦnosti ji poslal objektu, jen₧ je v²sledkem threadu. To proto, ₧e ukonΦenφ threadu objekt ArrayFault v pam∞ti tφmto v²sledkem nahradilo.
JAZYKOV┴ PODPORA
Jestli₧e majφ slu₧by HOM fungovat s libovoln²m p°φjemcem zprßv, je t°eba, aby zprßvy typu afterDelay Φi each byly k dispozici pro libovoln² objekt. To znamenß, ₧e chceme-li implementovat HOM, musφme mφt mo₧nost p°idßvat novΘ metody ke ko°enovΘ t°φd∞ (aby byly k dispozici kterΘmukoli objektu kterΘkoli t°φdy). V dob°e navr₧en²ch objektov²ch jazycφch to nenφ problΘm (Objective C k tomu mß tzv. kategorie). V jazycφch typu Javy, je₧ kategorie ani obdobnou slu₧bu nemajφ, to vÜak znamenß mφt mo₧nost zasßhnout p°φmo do zdrojov²ch k≤d∙ ko°enovΘ t°φdy.
Nutnostφ je, aby objektov² programovacφ jazyk podporoval polymorfismus, jin²mi slovy, aby bylo mo₧nΘ objektu zaslat libovolnou zprßvu s tφm, ₧e konkrΘtnφ zpracovßnφ tΘto zprßvy zßvisφ na objektu samotnΘm. To je d∙sledek toho, ₧e zßstupn² objekt (Delayed Performer) musφ b²t schopen p°ijmout jakoukoli zprßvu, nebo¥ ji sßm nezpracovßvß.
U jazyk∙ typu Javy nebo C++ zde op∞t narazφme - polymorfismus podporujφ pouze zΦßsti, jen v rßmci d∞dickΘ hierarchie t°φd (a v Jav∞ takΘ rozhranφ). Vytvo°it objekt, jen₧ p°ijφmß libovolnou zprßvu, v nich mo₧nΘ nenφ (v Jav∞ to mo₧nΘ je, pokud vyu₧ijeme slu₧by java.lang. reflect; problΘm vÜak je v tom, ₧e nem∙₧eme vyu₧φvat standardnφ syntaxi pro p°edßvßnφ zprßv a zßpis programu se komplikuje).
DalÜφ podmφnkou pro implementaci HOM je, aby bylo mo₧nΘ zprßvu vzφt a jako objekt ulo₧it do prom∞nnΘ (a odeslat pozd∞ji). Zde jde spφÜe o slu₧by standardnφch knihoven; v Jav∞ lze op∞t za cenu nepohodlφ vyu₧φt slu₧by java.lang.reflect. Standardnφ knihovny Objective C obdobnΘ slu₧by p°φmo nepodporujφ. Objective C se vÜak se standardnφmi knihovnami u₧ tΘm∞° nevyu₧φvß, b∞₧n∞ se pou₧φvajφ bohatÜφ knihovny zalo₧enΘ na standardu OpenStep (nap°. Cocoa). Tam je k dispozici t°φda NSInvocation, urΦenß k uklßdßnφ zprßv a jejich argument∙.
Poslednφ "trik", kter² musφ programovacφ jazyk pro implementaci HOM nabφzet, je zpracovßnφ obecnΘ zprßvy. To ·zce souvisφ s polymorfismem i s uklßdßnφm zprßv: mß-li objekt t°φdy DelayedPerformer b²t schopen p°ijmout (a ulo₧it a pozd∞ji p°edat) jakoukoli zprßvu, musφme mφt v jazyce nebo ve standardnφch knihovnßch podporu pro implementaci "neznßmΘ zprßvy".
V Objective C (s knihovnami a runtime zalo₧en²mi na standardu OpenStep) takovß podpora je. Standardn∞ tam platφ, ₧e pokud objekt dostane zprßvu, pro ni₧ nenφ v jeho t°φd∞ ani v ₧ßdnΘ z rodiΦovsk²ch t°φd definovßna metoda, runtime zprßvu i jejφ argumenty automaticky "zabalφ" do objektu t°φdy NSInvocation a ten cφlovΘmu objektu p°edß jako argument zprßvy forwardInvocation:.
Äßdnß ze souΦasn²ch verzφ Javy nic podobnΘho standardn∞ neobsahuje. Jeliko₧ ale Java p°φmo nepodporuje ani polymorfismus, musφme se p°i implementaci HOM stejn∞ obrßtit na vhodn² preprocesor a v rßmci jeho maker implementovat vlastnφ konvenci obdobnou zprßv∞ forwardInvocation: z Objective C.
Zßstupn² objekt typu fault je nahrazen jin²m objektem, tak₧e moduly, je₧ s objektem pracujφ, v∙bec "nepost°ehnou zm∞nu". To vy₧aduje zßsah do objektovΘho systΘmu na pom∞rn∞ nφzkΘ ·rovni. V Objective C je to mo₧nΘ dφky jeho "cΘΦkovΘmu" d∞dictvφ a dφky tomu, ₧e objektovß struktura je ve°ejn∞ zdokumentovßna (objekty obsahujφ odkaz na svou t°φdu a odkaz je na nφzkΘ ·rovni p°φstupn²). Bohu₧el, v Jav∞, pokud mi je znßmo, nic podobnΘho nenφ mo₧nΘ.
TO JE ZAT═M VèE...
Do tohoto Φlßnku se u₧ vφce informacφ o HOM nevejde. P°φÜt∞ si vÜak ukß₧eme konkrΘtnφ implementaci HOM a seznßmφme se i s n∞kter²mi praktick²mi p°φklady pou₧itφ. Vφce informacφ o HOM vΦetn∞ zdrojov²ch text∙ (anglicky) lze najφt na adrese www.metaobject.com.