Mengapa Anda membutuhkan proxy?
Pola ini membantu memecahkan masalah yang terkait dengan akses terkontrol ke suatu objek. Anda mungkin bertanya, "Mengapa kita membutuhkan akses terkontrol?" Mari kita lihat beberapa situasi yang akan membantu Anda mengetahui apa itu.Contoh 1
Bayangkan kita memiliki proyek besar dengan sekumpulan kode lama, di mana ada kelas yang bertanggung jawab untuk mengekspor laporan dari database. Kelas bekerja secara sinkron. Artinya, seluruh sistem tidak aktif saat database memproses permintaan. Rata-rata, dibutuhkan waktu 30 menit untuk menghasilkan laporan. Dengan demikian, proses ekspor dimulai pukul 12.30, dan manajemen menerima laporan pada pagi hari. Audit mengungkapkan bahwa akan lebih baik untuk dapat segera menerima laporan selama jam kerja normal. Waktu mulai tidak dapat ditunda, dan sistem tidak dapat memblokir saat menunggu respons dari database. Solusinya adalah mengubah cara kerja sistem, membuat dan mengekspor laporan di utas terpisah. Solusi ini akan membuat sistem berfungsi seperti biasa, dan manajemen akan menerima laporan baru. Namun, ada masalah: kode saat ini tidak dapat ditulis ulang, karena bagian lain dari sistem menggunakan fungsinya. Dalam hal ini, kita dapat menggunakan pola proxy untuk memperkenalkan kelas proxy perantara yang akan menerima permintaan untuk mengekspor laporan, mencatat waktu mulai, dan meluncurkan utas terpisah. Setelah laporan dibuat, utas diakhiri dan semua orang senang.Contoh 2
Tim pengembangan sedang membuat situs web acara. Untuk mendapatkan data tentang acara baru, tim meminta layanan pihak ketiga. Perpustakaan pribadi khusus memfasilitasi interaksi dengan layanan. Selama pengembangan, masalah ditemukan: sistem pihak ketiga memperbarui datanya sekali sehari, tetapi permintaan dikirim ke sana setiap kali pengguna menyegarkan halaman. Ini menciptakan banyak permintaan, dan layanan berhenti merespons. Solusinya adalah meng-cache respons layanan dan mengembalikan hasil cache ke pengunjung saat halaman dimuat ulang, memperbarui cache sesuai kebutuhan. Dalam hal ini, pola desain proxy adalah solusi terbaik yang tidak mengubah fungsionalitas yang ada.Prinsip di balik pola desain
Untuk menerapkan pola ini, Anda perlu membuat kelas proxy. Itu mengimplementasikan antarmuka kelas layanan, meniru perilakunya untuk kode klien. Dengan cara ini, klien berinteraksi dengan proxy alih-alih objek sebenarnya. Sebagai aturan, semua permintaan diteruskan ke kelas layanan, tetapi dengan tindakan tambahan sebelum atau sesudahnya. Sederhananya, proxy adalah lapisan antara kode klien dan objek target. Pertimbangkan contoh hasil kueri caching dari hard disk lama dan sangat lambat. Misalkan kita berbicara tentang jadwal kereta listrik di beberapa aplikasi kuno yang logikanya tidak dapat diubah. Disk dengan jadwal yang diperbarui dimasukkan setiap hari pada waktu yang tetap. Jadi kita punya:TrainTimetable
antarmuka.ElectricTrainTimetable
, yang mengimplementasikan antarmuka ini.- Kode klien berinteraksi dengan sistem file melalui kelas ini.
TimetableDisplay
kelas klien. MetodenyaprintTimetable()
menggunakan metode kelasElectricTrainTimetable
.
printTimetable()
, ElectricTrainTimetable
kelas mengakses disk, memuat data, dan mempresentasikannya ke klien. Sistem berfungsi dengan baik, tetapi sangat lambat. Akibatnya, keputusan dibuat untuk meningkatkan kinerja sistem dengan menambahkan mekanisme caching. Ini dapat dilakukan dengan menggunakan pola proxy: Dengan demikian, TimetableDisplay
kelas bahkan tidak menyadari bahwa ia berinteraksi dengan kelas, ElectricTrainTimetableProxy
bukan dengan kelas lama. Implementasi baru memuat jadwal sekali sehari. Untuk permintaan berulang, ini mengembalikan objek yang dimuat sebelumnya dari memori.
Tugas apa yang terbaik untuk proxy?
Berikut adalah beberapa situasi di mana pola ini pasti berguna:- Caching
- Tertunda, atau malas, inisialisasi Mengapa langsung memuat objek jika Anda dapat memuatnya sesuai kebutuhan?
- Mencatat permintaan
- Verifikasi antara data dan akses
- Memulai utas pekerja
- Merekam akses ke objek
Keuntungan dan kerugian
- + Anda dapat mengontrol akses ke objek layanan sesuka Anda
- + Kemampuan tambahan yang terkait dengan pengelolaan siklus hidup objek layanan
- + Ini berfungsi tanpa objek layanan
- + Ini meningkatkan kinerja dan keamanan kode.
- - Ada risiko kinerja menjadi lebih buruk karena permintaan tambahan
- - Itu membuat hierarki kelas lebih rumit
Pola proxy dalam praktek
Mari terapkan sistem yang membaca jadwal kereta dari hard disk:
public interface TrainTimetable {
String[] getTimetable();
String getTrainDepartureTime();
}
Inilah kelas yang mengimplementasikan antarmuka utama:
public class ElectricTrainTimetable implements TrainTimetable {
@Override
public String[] getTimetable() {
ArrayList<String> list = new ArrayList<>();
try {
Scanner scanner = new Scanner(new FileReader(new File("/tmp/electric_trains.csv")));
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
list.add(line);
}
} catch (IOException e) {
System.err.println("Error: " + e);
}
return list.toArray(new String[list.size()]);
}
@Override
public String getTrainDepartureTime(String trainId) {
String[] timetable = getTimetable();
for (int i = 0; i < timetable.length; i++) {
if (timetable[i].startsWith(trainId+";")) return timetable[i];
}
return "";
}
}
Setiap kali Anda mendapatkan jadwal kereta, program membaca file dari disk. Tapi itu hanya awal dari masalah kita. Seluruh file dibaca setiap kali Anda mendapatkan jadwal bahkan untuk satu kereta! Ada baiknya kode seperti itu hanya ada dalam contoh yang tidak boleh dilakukan :) Kelas klien:
public class TimetableDisplay {
private TrainTimetable trainTimetable = new ElectricTrainTimetable();
public void printTimetable() {
String[] timetable = trainTimetable.getTimetable();
String[] tmpArr;
System.out.println("Train\\tFrom\\tTo\\t\\tDeparture time\\tArrival time\\tTravel time");
for (int i = 0; i < timetable.length; i++) {
tmpArr = timetable[i].split(";");
System.out.printf("%s\t%s\t%s\t\t%s\t\t\t\t%s\t\t\t%s\n", tmpArr[0], tmpArr[1], tmpArr[2], tmpArr[3], tmpArr[4], tmpArr[5]);
}
}
}
Contoh berkas:
9B-6854;London;Prague;13:43;21:15;07:32
BA-1404;Paris;Graz;14:25;21:25;07:00
9B-8710;Prague;Vienna;04:48;08:49;04:01;
9B-8122;Prague;Graz;04:48;08:49;04:01
Mari kita uji:
public static void main(String[] args) {
TimetableDisplay timetableDisplay = new timetableDisplay();
timetableDisplay.printTimetable();
}
Keluaran:
Train From To Departure time Arrival time Travel time
9B-6854 London Prague 13:43 21:15 07:32
BA-1404 Paris Graz 14:25 21:25 07:00
9B-8710 Prague Vienna 04:48 08:49 04:01
9B-8122 Prague Graz 04:48 08:49 04:01
Sekarang mari kita telusuri langkah-langkah yang diperlukan untuk memperkenalkan pola kita:
-
Tentukan antarmuka yang memungkinkan penggunaan proxy alih-alih objek aslinya. Dalam contoh kita, ini adalah
TrainTimetable
. -
Buat kelas proxy. Itu harus memiliki referensi ke objek layanan (buat di kelas atau berikan ke konstruktor).
Inilah kelas proxy kami:
public class ElectricTrainTimetableProxy implements TrainTimetable { // Reference to the original object private TrainTimetable trainTimetable = new ElectricTrainTimetable(); private String[] timetableCache = null @Override public String[] getTimetable() { return trainTimetable.getTimetable(); } @Override public String getTrainDepartureTime(String trainId) { return trainTimetable.getTrainDepartureTime(trainId); } public void clearCache() { trainTimetable = null; } }
Pada tahap ini, kami hanya membuat kelas dengan referensi ke objek asli dan meneruskan semua panggilan ke sana.
-
Mari terapkan logika kelas proxy. Pada dasarnya, panggilan selalu dialihkan ke objek aslinya.
public class ElectricTrainTimetableProxy implements TrainTimetable { // Reference to the original object private TrainTimetable trainTimetable = new ElectricTrainTimetable(); private String[] timetableCache = null @Override public String[] getTimetable() { if (timetableCache == null) { timetableCache = trainTimetable.getTimetable(); } return timetableCache; } @Override public String getTrainDepartureTime(String trainId) { if (timetableCache == null) { timetableCache = trainTimetable.getTimetable(); } for (int i = 0; i < timetableCache.length; i++) { if (timetableCache[i].startsWith(trainId+";")) return timetableCache[i]; } return ""; } public void clearCache() { trainTimetable = null; } }
Pemeriksaan
getTimetable()
apakah larik jadwal telah di-cache di memori. Jika tidak, ia mengirimkan permintaan untuk memuat data dari disk dan menyimpan hasilnya. Jika jadwal telah diminta, dengan cepat mengembalikan objek dari memori.Berkat fungsinya yang sederhana, metode getTrainDepartureTime() tidak harus dialihkan ke objek aslinya. Kami hanya menggandakan fungsinya dalam metode baru.
Jangan lakukan ini. Jika Anda harus menggandakan kode atau melakukan sesuatu yang serupa, maka terjadi kesalahan, dan Anda perlu melihat masalahnya lagi dari sudut yang berbeda. Dalam contoh sederhana kami, kami tidak punya pilihan lain. Namun dalam proyek nyata, kode kemungkinan besar akan ditulis dengan lebih benar.
-
Dalam kode klien, buat objek proksi alih-alih objek aslinya:
public class TimetableDisplay { // Changed reference private TrainTimetable trainTimetable = new ElectricTrainTimetableProxy(); public void printTimetable() { String[] timetable = trainTimetable.getTimetable(); String[] tmpArr; System.out.println("Train\\tFrom\\tTo\\t\\tDeparture time\\tArrival time\\tTravel time"); for (int i = 0; i < timetable.length; i++) { tmpArr = timetable[i].split(";"); System.out.printf("%s\t%s\t%s\t\t%s\t\t\t\t%s\t\t\t%s\n", tmpArr[0], tmpArr[1], tmpArr[2], tmpArr[3], tmpArr[4], tmpArr[5]); } } }
Memeriksa
Train From To Departure time Arrival time Travel time 9B-6854 London Prague 13:43 21:15 07:32 BA-1404 Paris Graz 14:25 21:25 07:00 9B-8710 Prague Vienna 04:48 08:49 04:01 9B-8122 Prague Graz 04:48 08:49 04:01
Hebat, ini bekerja dengan benar.
Anda juga dapat mempertimbangkan opsi pabrik yang membuat objek asli dan objek proksi, bergantung pada kondisi tertentu.
GO TO FULL VERSION