Belajar Proxy Pattern dalam PHP

Ahmad Oriza 19 April 2020

Belajar Proxy Pattern dalam PHP

Konsep

Selamat datang kembali pada pembahasan design pattern. Kali ini kita akan belajar tentang Proxy Pattern. Pastinya kalian sering mendengar istilah Proxy. Terutama untuk teman-teman yang sering menggunakan VPN. Cara kerjanya mirip. Untuk mengakses sebuah website kita harus melewati dulu sebuah "penengah", sehingga apa yang kita buka tidak terlacak oleh ISP, karena disangka hanya sedang mengakses website "penengah". Padahal kita sedang mengakses website real yang diblokir oleh ISP.

Image

Jika kita hubungkan dengan dunia pemrograman, maka anggap saja "penengah" ini adalah sebuah class atau objek. Penengah ini disebut dengan proxy object. Disini alih-alih kita mengakses langsung real object, kita hanya mengakses Proxy. Tentunya karena adanya berbagai alasan.

Proxy pattern memiliki beberapa format penerapan diantaranya :

- Remote Proxy
- Virtual Proxy
- Protection Proxy
- Smart Reference

Namun disini kita akan coba pelajari hanya satu format, yaitu Smart Reference. Mudah-mudahan materi ini dapat terus diperbaharui.

Penasaran dengan cara kerja Proxy Pattern? mari kita praktek.

Praktek Tanpa Proxy

Pernahkah kamu menggunakan SDK/API atau apalah sebutannya, yang intinya itu adalah sebuah class. Kemudian kamu rasakan ketika memanggil sebuah method pada class tersebut terasa lambat sekali :

<?php /** * Misalnya ini adalah sebuah SDK/Class Twitter * Isi salah satu methodnya adalah getTweets * Misalnya saja class ini terdapat pada file terpisah. * Ini hanya untuk contoh ya :) */ interface TwitterInterface { public function getTweets(); } /** * Twitter API/SDK. */ class Twitter implements TwitterInterface { // Method ini berguna untuk mengambil public tweets dan ternyata response kadang terasa lama sekali. public function getTweets () { // Disini kode menjalankan REST API ke server Twitter untuk mengambil data Twitter. echo "Connecting to server .. \n"; echo "Getting tweets .. \n"; echo "Done .. \n"; $outputSample = [ [ 'username' => '@budi', 'tweet' => 'Selamat pagi!', ], [ 'username' => '@sarah', 'tweet' => 'Selamat malam!', ], ]; return $outputSample; } } // Instansiasi. $Twitter = new Twitter; // Jalankan getTweets() print_r($Twitter->getTweets()); ?>

Output :

Connecting to server .. Getting tweets .. Done .. Array ( [0] => Array ( [username] => @budi [tweet] => Selamat pagi! ) [1] => Array ( [username] => @sarah [tweet] => Selamat malam! ) )

Pada kode diatas ceritanya kita menggunakan library pihak ketiga. Kita include class ny pada core program, lalu membuat objek Twitter. Kemudian kita jalankan method getTweets() untuk mengambil public Tweet dari Twitter.

Pada praktiknya ternyata method getTweet() ini terasa tidak konsisten. Execution timenya berubah ubah, dan lebih parah lagi tidak menghasilkan output apapun, karena network latency yang tinggi. Hal ini mengakibatkan data response kadang kala kosong dan kita tidak bisa menampilkannya pada website/sistem.

Nah, disini Proxy Pattern hadir untuk kita. Kita bisa menyelesaikan masalah ini kita bisa menerapkan Smart Reference. Bagaimana sih caranya?

Praktek dengan Proxy

Caranya relatif mudah, kita cukup membuat sebuah class Proxy dengan menambahkan logika Cache di dalamnya. Mari kita lakukan step by stepnya.

Pertama-tama siapkan class yang berguna untuk caching terlebih dahulu. Misalnya disini saya contohkan dengan Redis. Sebenarnya kita juga bisa menggunakan sebuah table pada Database. Penerapan real bisa berbeda. Mari lihat kode berikut :

/** * Redis Cache. */ class Redis { public function set($key, $value, $expiredTime) { echo "Saving data to cache .. \n"; } public function get($key) { echo "Returning data from cache .. \n"; } }

Kedua, baru kita membuat sebuah class Proxy :

/** * Membuat Proxy. */ class TwitterProxy implements TwitterInterface{ /* Kita menggunakan nama global, jangan langsung menggunakan nama konkrit objek Twitter kita ganti dengan property `api`. Redis kita ganti dengan property `cache` Hal ini agar class Proxy dapat lebih independen. Untuk memudahkan maintanance jika berganti objek konkrit. */ protected $api; protected $cache; // Menambahkan konstruktor dengan inject objek Twitter dan Redis. public function __construct(Twitter $twitter, Redis $redis) { $this->api = $twitter; $this->cache = $redis; } // Method getTweets kita modifikasi. public function getTweets () { // Cek dulu sudah ada data yang di cache? // Ceritanya key nya adalah "Tweets" if ($cached = $this->cache->get('Tweets')) { // Keluarkan output simulasi .. echo "Getting from cache .. \n"; echo "Getting tweets .. \n"; echo "Done .. \n"; // Kembalikan cached, tidak usah ambil data lagi. return $cached; } // Kalau belum ada, baru ambil. $result = $this->api->getTweets(); // Jangan lupa masukan ke cache, lalu kita set expired timenya. Supaya bisa ambil data baru lagi. $this->cache->set('Tweets', $result, 3600); return $result; } }

Penjelasannya sudah saya sisipkan pada setiap kode. Intinya kita membuat sebuah class Proxy yang mengkonsumsi objek Twitter dan Redis. Kita disitu membuat sebuah method getTweets() dengan kemampuan caching. Jika data pada cache ditemukan maka ambil saja dari cache. Jika tidak ada baru jalankan real object method. Dengan begitu, dalam beberapa rentang waktu kita tidak perlu takut koneksi gagal.

Sekarang mari kita lihat kode utuhnya. Disini saya coba masukan kode dalam satu file agar lebih sederhana. Harusnya pada praktek real, kode real object, proxy, dan cache terdapat pada file terpisah :

<?php /** * Misalnya ini adalah sebuah SDK/Class Twitter * Isi salah satu methodnya adalah getTweets * Misalnya saja class ini terdapat pada file terpisah. */ interface TwitterInterface { public function getTweets(); } /** * Twitter API/SDK. */ class Twitter implements TwitterInterface { // Method ini berguna untuk mengambil public tweets dan ternyata response kadang terasa lama sekali. public function getTweets () { // Disini kode menjalankan REST API ke server Twitter untuk mengambil data Twitter. echo "Connecting to server .. \n"; echo "Getting tweets .. \n"; echo "Done .. \n"; $outputSample = [ [ 'username' => '@budi', 'tweet' => 'Selamat pagi!', ], [ 'username' => '@sarah', 'tweet' => 'Selamat malam!', ], ]; return $outputSample; } } /** * Redis Cache. */ class Redis { public function set($key, $value, $expiredTime) { echo "Saving data to cache .. \n"; } public function get($key) { echo "Returning data from cache .. \n"; } } /** * Membuat Proxy. */ class TwitterProxy implements TwitterInterface{ /* Kita menggunakan nama global, jangan langsung menggunakan nama konkrit objek Twitter kita ganti API, Redis kita ganti Cache Untuk memudahkan maintanance jika berganti objek konkrit. */ protected $api; protected $cache; // Menambahkan konstruktor dengan inject objek Twitter dan Redis. public function __construct(Twitter $twitter, Redis $redis) { $this->api = $twitter; $this->cache = $redis; } // Method getTweets kita modifikasi. public function getTweets () { // Cek dulu sudah ada data yang di cache? // Ceritanya key nya adalah "Tweets" if ($cached = $this->cache->get('Tweets')) { // Keluarkan output simulasi .. echo "Getting from cache .. \n"; echo "Getting tweets .. \n"; echo "Done .. \n"; // Kembalikan cached, tidak usah ambil data lagi. return $cached; } // Kalau belum ada, baru ambil. $result = $this->api->getTweets(); // Jangan lupa masukan ke cache, lalu kita set expired timenya. Supaya bisa ambil data baru lagi. $this->cache->set('Tweets', $result, 3600); return $result; } } /* Cara lama tanpa Proxy --------------------- $Twitter = new Twitter; print_r($Twitter->getTweets()); */ /* Dengan Proxy */ // Konsumsi Proxy, bukan objek konkrit. // Bangun objek dengan konstruktur dependency. $Twitter = new TwitterProxy(new Twitter, new Redis); print_r($Twitter->getTweets()); ?>

Output :

Returning data from cache .. Connecting to server .. Getting tweets .. Done .. Saving data to cache .. Array ( [0] => Array ( [username] => @budi [tweet] => Selamat pagi! ) [1] => Array ( [username] => @sarah [tweet] => Selamat malam! ) )

Pada kode diatas kita tidak lagi menggunakan objek kontrit Twitter. Kita sudah menggunakan objek Proxy untuk menjalankan getTweets(). Disitu kita membuat juga menerapkan konstruktor Objek Twitter dan Caching, dengan sedikit bumbu konsep Dependency Injection.

Coba perhatikan lebih lanjut. Objek proxy dan konkrit menerapkan Interface yang sama. Hal ini merupakan aturan yang disarankan dalam konsep pattern ini. Hal ini bertujuan agar kedua objek tersebut bersifat Interchangeable, atau dapat saling menggantikan satu sama lain.

Perlu dicatat, ketika menerapkan proxy pattern, sebuah objek proxy diharuskan hanya fokus pada sebuah unit saja. Jangan sampai class proxy bertugas menggantikan berbagai class konkrit. Hal tersebut sudah menyalahi aturan proxy pattern. Dan tidak bisa disebut dengan proxy lagi.

Demikianlah materi Smart Reference, Proxy Pattern. Semoga dapat dipahami.

Referensi :