/ janturon.cz / Od kodéra k analytikovi / Proxy, Líná inicializace, Preprocesor

Proxy - Líná inicializace - Preprocesor

Narozdíl od vzoru Dekorátor, kde k volané metodě něco dodáváme, vzor Proxy se používá tam, kde chceme před vykonáním metody provést dodatečnou kontrolu (a pokud není splněna, případně zavolat úplně jinou metodu). Podle důvodů k tomuto počínání dělíme proxy na:

Ověřovací proxy

Mějme metodu, která zobrazí tajná data:

// C++ void secret() { cout << "tajna data"; }; // Javascript function secret() { alert("tajna data"); }

Ověřovací proxy nám umožní zajistit přístup pouze autorizovanému uživateli.

// C++ bool authorized = false; bool authorize() { cout << "Zadej heslo: "; string password; getline(cin,password,'\n'); return password == "iddqd"; } string secretProxy() { if(!authorized) authorized = authorize(); return authorized ? secret() : "pristup odepren"; } // Javascipt var authorized = false; function authorize() { return prompt("Zadej heslo:") == "iddqd"; } function secretProxy() { if(!authorized) authorized = authorize(); return authorized ? secret() : "pristup odepren"; }

Tím jsme naprogramovali Proxy spolu s antivzorem Big ball of mud - kód bez jakékoliv rozpoznatelné struktury. Pomocí abstrakce oddělíme obecné (které lze použít i jinde) od nahodilého (to, co se mění), což jsou v tomto případě funkce pro zobrazení tajných dat a pro autorizaci uživatele. Obecné jest:

// C++ class Proxy { bool authorized; function<bool()> test; public: Proxy(function<bool()> test) { authorized = false; this->test = test; } void Access(function<void()> target) { if(!authorized) authorized = test(); if(authorized) target(); } }; // Javascipt function Proxy(test) { var authorized = false; this.Access = function(target) { if(!authorized) authorized = test(); if(authorized) target(); } };

Což můžeme uložit jako opakovatelně využitelnou třídu, k jejímuž volání lze přidat ono nahodilé:

// C++ void secret() { cout << "\ntajna data"; } bool authorize() { cout << "zadej heslo: "; string password; getline(cin,password,'\n'); return password == "iddqd"; } int main() { Proxy proxy(authorize); proxy.Access(secret); proxy.Access(secret); return 0; } // Javascipt function secret() { alert("tajna data"); } function authorize() { return prompt("Zadej heslo:") == "iddqd"; } var proxy = new Proxy(authorize); proxy.Access(secret); proxy.Access(secret);

Program dvakrát přistupuje k autorizovaným datům, která vypíše jen po zadání správného hesla. Od chvíle prvního úspěšného zadání se už na heslo neptá.

Kód je asi o polovinu jednodušší: kdyby byl vznesen požadavek na možnost resetování Proxy po deseti minutách, víme, že stačí upravit kód třídy Proxy (na jednom místě). Kdybychom měli změnit dialog z textového na grafický (nebo jinou úpravu v main), víme, že do kódu Proxy nemusíme zasahovat.

Až od chvíle, kdy jsme provedli toto oddělení (decoupling), je oprávněné označovat tento postup jako "návrhový vzor". Protože vysvětlení toho bylo zdlouhavé a následující verze Proxy jsou velmi podobné, přeskočím úplný výklad a vysvětlím je až po onu Velkou hroudu bahna (jako v úvodu tohoto příkladu). Odstranění toho antivzoru opět proveďte Abstrakcí.

Líná Inicializace (virtuální proxy)

Předpokládejme, že pro čtení dat je zapotřebí připojit se k serveru (řekněme, že funkcí Connect()), který z bezpečnostních důvodů vrátí odpověď až po několika sekundách. Proxy, která by nebyla líná, provede připojení na začátku programu:

// C++ int main() { Connect(); Proxy proxy(test); proxy.Access(secret); ... } // Javascipt Connect(); var proxy = new Proxy(test); proxy.Access(secret); ...

"Lenost" spočívá v tom, že připojení co nejvíce oddálíme: připojíme se až ve chvíli prvního požadavku na data:

// C++ bool connected = false; void secret() { if(!connected) Connect(); cout << "\ntajna data"; } // Javascipt var connected = false; function secret() { if(!connected) Connect(); alert("tajna data"); }

Tedy v případě, že uživatel nezná heslo, k pokusu o připojení vůbec nedojde, aplikace je tak rychlejší a server méně vytíženější. Alternativně lze tuto lenost řešit na serveru: ten si může uchovávat tabulku IP adres a bezpečnostní prodlevu zařadit každé IP adrese jednou za deset minut.

Tenký a tlustý klient

Při komunikaci klient-server tak máme dvě možnosti přístupu:

Preprocesor

Mějme funkci tisknoucí rozsah čísel

// C++ void printRange(int from, int to) { for(int i=from; i!=to; i++) cout << i << " "; cout << to; } // Javascipt function printRange(from,to) { var result = ""; for(var i=from; i!=to; i++) result+= i + " "; result+= to; alert(result); }

Tato funkce skončí zacyklením, pokud bude from>to. Vytvoříme preprocesor, který v takovém případě prohodí argumenty:

// C++ void printRangeProxy(int from, int to) { if(from<=to) printRange(from,to); else printRange(to,from); } // Javascipt function printRangeProxy(from,to) { if(from<=to) printRange(from,to); else printRange(to,from); }