Mengapa Anda membutuhkan Wakil?
Pola ini membantu memecahkan masalah yang terkait dengan akses terkontrol ke suatu objek. Anda mungkin memiliki pertanyaan: “Mengapa kita memerlukan akses terkontrol seperti itu?” 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 terdapat kelas yang bertanggung jawab untuk mengunduh laporan dari database. Kelas bekerja secara sinkron, yaitu seluruh sistem dalam keadaan idle saat database memproses permintaan. Rata-rata, laporan dihasilkan dalam 30 menit. Karena fitur ini, pengunggahannya dimulai pada 00:30, dan manajemen menerima laporan ini pada pagi hari. Selama analisis, ternyata laporan perlu diterima segera setelah dibuat, yaitu dalam waktu satu hari. Tidak mungkin menjadwal ulang waktu mulai, karena sistem akan menunggu respons dari database. Solusinya adalah mengubah prinsip pengoperasian dengan memulai pengunggahan dan pembuatan laporan di thread terpisah. Solusi ini akan memungkinkan sistem beroperasi seperti biasa, dan manajemen akan menerima laporan baru. Namun, terdapat masalah: kode saat ini tidak dapat ditulis ulang, karena fungsinya digunakan oleh bagian lain dari sistem. Dalam hal ini, Anda dapat memperkenalkan kelas proksi perantara menggunakan pola Deputi, yang akan menerima permintaan untuk mengunggah laporan, mencatat waktu mulai, dan meluncurkan thread terpisah. Saat laporan dibuat, thread akan menyelesaikan tugasnya dan semua orang akan senang.Contoh 2
Tim pengembangan membuat situs web poster. Untuk memperoleh data tentang peristiwa baru, mereka beralih ke layanan pihak ketiga, interaksi yang dilakukan melalui perpustakaan tertutup khusus. Selama pengembangan, masalah muncul: sistem pihak ketiga memperbarui data sekali sehari, dan permintaan untuk data tersebut terjadi setiap kali pengguna menyegarkan halaman. Hal ini menciptakan sejumlah besar permintaan dan layanan berhenti merespons. Solusinya adalah dengan menyimpan respons layanan dalam cache dan memberikan hasil yang disimpan kepada pengunjung pada setiap reboot, memperbarui cache ini sesuai kebutuhan. Dalam hal ini, menggunakan pola Deputi adalah solusi terbaik tanpa mengubah fungsionalitas akhir.Bagaimana polanya bekerja
Untuk mengimplementasikan pola ini, Anda perlu membuat kelas proxy. Ini mengimplementasikan antarmuka kelas layanan, mensimulasikan perilakunya untuk kode klien. Jadi, alih-alih objek sebenarnya, klien berinteraksi dengan proxy-nya. Biasanya, semua permintaan diteruskan ke kelas layanan, tetapi dengan tindakan tambahan sebelum atau sesudah pemanggilannya. Sederhananya, objek proxy ini adalah lapisan antara kode klien dan objek target. Mari kita lihat contoh permintaan cache dari disk lama yang sangat lambat. Biarlah itu menjadi jadwal kereta listrik dalam beberapa aplikasi kuno, yang prinsip pengoperasiannya tidak dapat diubah. Disk dengan jadwal yang diperbarui dimasukkan setiap hari pada waktu yang tetap. Jadi kita punya:- Antarmuka
TimetableTrains
. - Kelas
TimetableElectricTrains
yang mengimplementasikan antarmuka ini. - Melalui kelas inilah kode klien berinteraksi dengan sistem file disk.
- Kelas klien
DisplayTimetable
. MetodenyaprintTimetable()
menggunakan metode kelasTimetableElectricTrains
.
printTimetable()
kelas TimetableElectricTrains
mengakses disk, mengeluarkan data dan menyediakannya ke klien. Sistem ini bekerja dengan baik, namun sangat lambat. Oleh karena itu, diputuskan untuk meningkatkan kinerja sistem dengan menambahkan mekanisme caching. Hal ini dapat dilakukan dengan menggunakan pola Proxy: Dengan cara ini kelas DisplayTimetable
tidak akan menyadari bahwa ia sedang berinteraksi dengan kelas tersebut TimetableElectricTrainsProxy
dan bukan dengan kelas sebelumnya. Implementasi baru memuat jadwal sekali sehari, dan berdasarkan permintaan berulang, mengembalikan objek yang sudah dimuat dari memori.
Untuk tugas apa lebih baik menggunakan Proxy?
Berikut adalah beberapa situasi di mana pola ini pasti akan berguna:- cache.
- Implementasi yang malas disebut juga dengan implementasi yang malas. Mengapa memuat suatu objek sekaligus ketika Anda dapat memuatnya sesuai kebutuhan?
- Permintaan pencatatan.
- Pemeriksaan data dan akses sementara.
- Meluncurkan thread pemrosesan paralel.
- Merekam atau menghitung riwayat panggilan.
Keuntungan dan kerugian
- + Anda dapat mengontrol akses ke objek layanan sesuai keinginan;
- + Kemampuan tambahan untuk mengelola siklus hidup objek layanan;
- + Bekerja tanpa objek layanan;
- + Meningkatkan kinerja dan keamanan kode.
- - Ada risiko penurunan kinerja akibat perawatan tambahan;
- - Memperumit struktur kelas program.
Gantikan pola dalam praktik
Mari kita terapkan sistem yang membaca jadwal kereta dari disk:public interface TimetableTrains {
String[] getTimetable();
String getTrainDepartureTime();
}
Kelas yang mengimplementasikan antarmuka utama:
public class TimetableElectricTrains implements TimetableTrains {
@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 mencoba mendapatkan jadwal semua kereta, program membaca file dari disk. Tapi ini tetaplah bunga. File tersebut juga dibaca setiap kali Anda perlu mendapatkan jadwal satu kereta saja! Ada baiknya kode seperti itu hanya ada di contoh yang buruk :) Kelas klien:
public class DisplayTimetable {
private TimetableTrains timetableTrains = new TimetableElectricTrains();
public void printTimetable() {
String[] timetable = timetableTrains.getTimetable();
String[] tmpArr;
System.out.println("Поезд\tОткуда\tКуда\t\tВремя отправления\tВремя прибытия\tВремя в пути");
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;Лондон;Прага;13:43;21:15;07:32
BA-1404;Париж;Грац;14:25;21:25;07:00
9B-8710;Прага;Вена;04:48;08:49;04:01;
9B-8122;Прага;Грац;04:48;08:49;04:01
Mari kita uji:
public static void main(String[] args) {
DisplayTimetable displayTimetable = new DisplayTimetable();
displayTimetable.printTimetable();
}
Kesimpulan:
Поезд Откуда Куда Время отправления Время прибытия Время в пути
9B-6854 Лондон Прага 13:43 21:15 07:32
BA-1404 Париж Грац 14:25 21:25 07:00
9B-8710 Прага Вена 04:48 08:49 04:01
9B-8122 Прага Грац 04:48 08:49 04:01
Sekarang mari kita melalui langkah-langkah penerapan pola kita:
-
Tentukan antarmuka yang memungkinkan Anda menggunakan proxy baru, bukan objek aslinya. Dalam contoh kita ini adalah
TimetableTrains
. -
Buat kelas proksi. Itu harus berisi referensi ke objek layanan (buat di kelas atau lewati konstruktor);
Inilah kelas proxy kami:
public class TimetableElectricTrainsProxy implements TimetableTrains { // Ссылка на оригинальный an object private TimetableTrains timetableTrains = new TimetableElectricTrains(); private String[] timetableCache = null @Override public String[] getTimetable() { return timetableTrains.getTimetable(); } @Override public String getTrainDepartureTime(String trainId) { return timetableTrains.getTrainDepartureTime(trainId); } public void clearCache() { timetableTrains = null; } }
Pada tahap ini, kita cukup membuat kelas dengan referensi ke objek asli dan meneruskan semua panggilan ke objek tersebut.
-
Kami menerapkan logika kelas proxy. Pada dasarnya panggilan selalu diarahkan ke objek aslinya.
public class TimetableElectricTrainsProxy implements TimetableTrains { // Ссылка на оригинальный an object private TimetableTrains timetableTrains = new TimetableElectricTrains(); private String[] timetableCache = null @Override public String[] getTimetable() { if(timetableCache == null) { timetableCache = timetableTrains.getTimetable(); } return timetableCache; } @Override public String getTrainDepartureTime(String trainId) { if(timetableCache == null) { timetableCache = timetableTrains.getTimetable(); } for(int i = 0; i < timetableCache.length; i++) { if(timetableCache[i].startsWith(trainId+";")) return timetableCache[i]; } return ""; } public void clearCache() { timetableTrains = null; } }
Metode ini
getTimetable()
memeriksa apakah susunan jadwal di-cache di memori. Jika tidak, ia mengeluarkan permintaan untuk memuat data dari disk, menyimpan hasilnya. Jika permintaan sudah berjalan, maka dengan cepat akan mengembalikan objek dari memori.Berkat fungsinya yang sederhana, metode getTrainDepartireTime() tidak perlu dialihkan ke objek aslinya. Kami hanya menduplikasi fungsinya menjadi metode baru.
Anda tidak bisa melakukan itu. Jika Anda harus menduplikasi kode atau melakukan manipulasi serupa, itu berarti ada yang tidak beres dan Anda perlu melihat masalahnya dari sudut yang berbeda. Dalam contoh sederhana kami, tidak ada cara lain, tetapi dalam proyek nyata, kemungkinan besar, kode akan ditulis dengan lebih benar.
-
Ganti pembuatan objek asli dalam kode klien dengan objek pengganti:
public class DisplayTimetable { // Измененная link private TimetableTrains timetableTrains = new TimetableElectricTrainsProxy(); public void printTimetable() { String[] timetable = timetableTrains.getTimetable(); String[] tmpArr; System.out.println("Поезд\tОткуда\tКуда\t\tВремя отправления\tВремя прибытия\tВремя в пути"); 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]); } } }
Penyelidikan
Поезд Откуда Куда Время отправления Время прибытия Время в пути 9B-6854 Лондон Прага 13:43 21:15 07:32 BA-1404 Париж Грац 14:25 21:25 07:00 9B-8710 Прага Вена 04:48 08:49 04:01 9B-8122 Прага Грац 04:48 08:49 04:01
Bagus, ini berfungsi dengan benar.
Anda juga dapat mempertimbangkan pabrik yang akan membuat objek asli dan objek pengganti tergantung pada kondisi tertentu.
Tautan yang berguna, bukan titik
-
Artikel bagus tentang pola dan sedikit tentang "Deputi"
GO TO FULL VERSION