A Proxy Pattern szerepe az ókori rómában
A mai témánk egy igen egyszerű, ám hasznos tervezési minta lesz, mégpedig a proxy. A név sokaknak
ismerős lehet, akik próbáltak már céges netről betyárkodni és a dolog nem akart sehogy se összejönni nekik, mert egy bizonyos proxy lépett be a képbe. Na de mi is ez és mi célt szolgál? Cikkünkből megtudhatod!
Ha fellapozzuk a révai nagylexikont a proxy szervernél, akkor azt olvashatjuk, hogy ez beépül a webszerver és a kliens közé és a kliens nevében kéréseket eszközöl a webszerver felé, gyorsítótáraz, megváltoztatja a kliens kérését vagy éppen a webhely válaszát. Akkor most jöjjön az, ami számunkra fontos:
A proxy pattern a struktúrális minták közé tartozik, lényegében egy olyan osztály lesz, mely interfészében azonos a fogadó osztállyal, ezáltal nem megkülönböztethető, a kliens nem tudja, hogy ő “csak egy proxyhoz” kapcsolódott. A proxy a metódusaival forwardolja a kéréseket és visszaadja a fogadó osztály válaszát.
Most akkor csináljuk meg a fent említett webszerver/kliens/proxy triumvirátust a proxy pattern elemeit bemutatva!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Client { // ezek vagyunk mi
private $server;
public function __construct(ServerInterface $server) { // typehinteljük az interfészt
$this->server = $server; // sima konstruktor injectionnel adjuk át a szerver példányát
}
public function request($method, $url, $parameters) {
$this->server->incomingConnection($method, $url, $parameters); // nagyon leegyszerüsítve a dolog
}
}
Ebben eddig semmi érdekes nem volt, typehinteltünk a konstruktorban egy interfészt, így azon osztályok, amik implementálják azt az interfészt, hiba nélkül átadhatóak. Itt lesz a mi kis trükkünk, mivel mind a proxy, mind a webszerver osztályunk egyaránt implementálja majd az interfészt, ezáltal mindkettő használható lesz.
1
2
3
4
5
interface ServerInterface {
public function incomingConnection($method, $url, $parameters); // az interfészünk elég egyszerű lesz, egy metódust vittünk most bele
}
Nem is volt nehéz, ugye? Na de implementáljuk is ezt az interfészt a szerverünkben
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class WebServer implements ServerInterface { // a webszerver osztályunk
public function incomingConnection($method, $url, $parameters) {
// mindenféle feketemágia, aminek révén visszaadunk egy választ
return $response;
}
}
class ProxyServer implements ServerInterface { // a proxy osztályunk, ami szintén implementálja az adott interfészt
public function __construct() {
$this->server = new WebServer(); // héé, ez már ismerős... a proxy elrakja magának a szerver egy példányát, mivel azon keresztül fog működni
}
public function incomingConnection($method, $url, $parameters) { // ugye a kötelező metódus
return $this->server->incomingConnection($method, $url, $parameters); // szimplán forwardoljuk a dolgot
}
}
A fenti példában jól látni miről is szól a proxy patternünk. Meghívunk egy osztályt, ami önmagában foglalja azt az osztályt, amit mi el akarunk érni és a kéréseinket továbbítja neki.
Ezeket a kéréseket azonban meg lehet változtatni, valamint a válaszokat is, hiszen a proxy-n keresztülmegy a kérés.
Ha egyes osztályaink felé le akarjuk korlátozni a hozzáférést, akkor ilyen proxy-kon keresztül tudjuk elérni azt. Persze gyorsítótárazhatunk is, megvizsgálhatjuk ezt a kérést és még sok mást lehet beleszuszakolni ebbe a mintába, de most nézzünk pár példát mit lehet beletűzdelni:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ProxyServer implements ServerInterface {
public function __construct(WebServer $server, CacheInterface $cache) {
// valamilyen cache-t is hozzáadunk és DI-zzük a szerverünket
$this->server = $server
$this->cache = $cache;
}
public function incomingConnection($method, $url, $parameters) {
if (!$this->allowed) return $this->getErrorPage(403); // ha nem engedélyezett, akkor a saját 403-as oldalunkat adjuk vissza
$key = md5($method, $url, $parameters); // valami egyedi azonosítót generálunk, ennek a módszerét rátok bízom
if ($this->cache->has($key)) {
return $this->cache->get($key); // visszaadjuk cache-ből
} else {
$this->cache->set($key, $this->server->incomingConnection($method, $url, $parameters); // lementjük cache-be
return $this->cache->get($key); // aztán visszaadjuk onnan
}
}
}
Ezek csak szimpla metódusok, sokminden variálható, de ne feledjük, hogy ez csak egy helyettes osztály, így ne vegye át a másik osztályunk munkáját, csupán a szerepét.