Mengapa anda memerlukan proksi?
Corak ini membantu menyelesaikan masalah yang berkaitan dengan akses terkawal kepada objek. Anda mungkin bertanya, "Mengapa kami memerlukan akses terkawal?" Mari lihat beberapa situasi yang akan membantu anda mengetahui apa itu.Contoh 1
Bayangkan kita mempunyai projek besar dengan sekumpulan kod lama, di mana terdapat kelas yang bertanggungjawab untuk mengeksport laporan daripada pangkalan data. Kelas berfungsi serentak. Iaitu, keseluruhan sistem melahu sementara pangkalan data memproses permintaan. Secara purata, ia mengambil masa 30 minit untuk menghasilkan laporan. Sehubungan itu, proses eksport bermula pada 12:30 AM, dan pihak pengurusan menerima laporan pada waktu pagi. Audit mendedahkan bahawa adalah lebih baik untuk menerima laporan dengan segera pada waktu perniagaan biasa. Masa mula tidak boleh ditangguhkan, dan sistem tidak boleh menyekat sementara ia menunggu respons daripada pangkalan data. Penyelesaiannya ialah mengubah cara sistem berfungsi, menjana dan mengeksport laporan pada urutan yang berasingan. Penyelesaian ini akan membolehkan sistem berfungsi seperti biasa dan pengurusan akan menerima laporan baharu. Walau bagaimanapun, terdapat masalah: kod semasa tidak boleh ditulis semula, kerana bahagian lain sistem menggunakan fungsinya. Dalam kes ini, kita boleh menggunakan corak proksi untuk memperkenalkan kelas proksi perantaraan yang akan menerima permintaan untuk mengeksport laporan, log masa mula dan melancarkan urutan yang berasingan. Setelah laporan dijana, rangkaian ditamatkan dan semua orang gembira.Contoh 2
Pasukan pembangunan sedang mencipta tapak web acara. Untuk mendapatkan data tentang acara baharu, pasukan bertanyakan perkhidmatan pihak ketiga. Perpustakaan persendirian khas memudahkan interaksi dengan perkhidmatan. Semasa pembangunan, masalah ditemui: sistem pihak ketiga mengemas kini datanya sekali sehari, tetapi permintaan dihantar kepadanya setiap kali pengguna memuat semula halaman. Ini menghasilkan sejumlah besar permintaan, dan perkhidmatan berhenti bertindak balas. Penyelesaiannya adalah untuk cache respons perkhidmatan dan mengembalikan hasil cache kepada pelawat apabila halaman dimuatkan semula, mengemas kini cache seperti yang diperlukan. Dalam kes ini, corak reka bentuk proksi adalah penyelesaian terbaik yang tidak mengubah fungsi sedia ada.Prinsip di sebalik corak reka bentuk
Untuk melaksanakan corak ini, anda perlu membuat kelas proksi. Ia melaksanakan antara muka kelas perkhidmatan, meniru tingkah lakunya untuk kod klien. Dengan cara ini, pelanggan berinteraksi dengan proksi dan bukannya objek sebenar. Sebagai peraturan, semua permintaan dihantar ke kelas perkhidmatan, tetapi dengan tindakan tambahan sebelum atau selepas. Ringkasnya, proksi ialah lapisan antara kod klien dan objek sasaran. Pertimbangkan contoh caching hasil pertanyaan daripada cakera keras lama dan sangat perlahan. Katakan kita bercakap tentang jadual untuk kereta api elektrik dalam beberapa aplikasi purba yang logiknya tidak boleh diubah. Cakera dengan jadual waktu yang dikemas kini dimasukkan setiap hari pada masa yang tetap. Jadi, kami ada:TrainTimetable
antara muka.ElectricTrainTimetable
, yang melaksanakan antara muka ini.- Kod klien berinteraksi dengan sistem fail melalui kelas ini.
TimetableDisplay
kelas pelanggan. KaedahnyaprintTimetable()
menggunakan kaedah kelasElectricTrainTimetable
.

printTimetable()
, ElectricTrainTimetable
kelas mengakses cakera, memuatkan data, dan membentangkannya kepada pelanggan. Sistem berfungsi dengan baik, tetapi ia sangat perlahan. Akibatnya, keputusan dibuat untuk meningkatkan prestasi sistem dengan menambah mekanisme caching. Ini boleh dilakukan menggunakan corak proksi: 
TimetableDisplay
kelas tidak menyedari bahawa ia berinteraksi dengan kelas ElectricTrainTimetableProxy
dan bukannya kelas lama. Pelaksanaan baharu memuatkan jadual waktu sekali sehari. Untuk permintaan ulangan, ia mengembalikan objek yang dimuatkan sebelum ini daripada memori.
Apakah tugas yang terbaik untuk proksi?
Berikut ialah beberapa situasi di mana corak ini pasti berguna:- Caching
- Permulaan tertangguh atau malas Mengapa memuatkan objek dengan segera jika anda boleh memuatkannya mengikut keperluan?
- Permintaan pembalakan
- Pengesahan perantaraan data dan akses
- Memulakan benang pekerja
- Merakam akses kepada objek
Kelebihan dan kekurangan
- + Anda boleh mengawal akses kepada objek perkhidmatan mengikut kehendak anda
- + Kebolehan tambahan yang berkaitan dengan menguruskan kitaran hayat objek perkhidmatan
- + Ia berfungsi tanpa objek perkhidmatan
- + Ia meningkatkan prestasi dan keselamatan kod.
- - Terdapat risiko bahawa prestasi mungkin menjadi lebih teruk disebabkan permintaan tambahan
- - Ia menjadikan hierarki kelas lebih rumit
Corak proksi dalam amalan
Mari kita laksanakan sistem yang membaca jadual waktu kereta api daripada cakera keras:
public interface TrainTimetable {
String[] getTimetable();
String getTrainDepartureTime();
}
Berikut ialah kelas yang melaksanakan antara muka 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 mendapat jadual waktu kereta api, program membaca fail dari cakera. Tetapi itu hanya permulaan masalah kami. Keseluruhan fail dibaca setiap kali anda mendapat jadual waktu untuk kereta api tunggal! Adalah baik bahawa kod sedemikian wujud hanya dalam contoh perkara yang tidak boleh dilakukan :) Kelas pelanggan:
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 fail:
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();
}
Pengeluaran:
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 ikuti langkah-langkah yang diperlukan untuk memperkenalkan corak kami:
-
Tentukan antara muka yang membenarkan penggunaan proksi dan bukannya objek asal. Dalam contoh kami, ini adalah
TrainTimetable
. -
Buat kelas proksi. Ia sepatutnya mempunyai rujukan kepada objek perkhidmatan (buat dalam kelas atau hantar kepada pembina).
Inilah kelas proksi 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 peringkat ini, kami hanya mencipta kelas dengan rujukan kepada objek asal dan memajukan semua panggilan kepadanya.
-
Mari kita laksanakan logik kelas proksi. Pada asasnya, panggilan sentiasa diubah hala ke objek asal.
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; } }
Semakan
getTimetable()
sama ada tatasusunan jadual waktu telah dicache dalam ingatan. Jika tidak, ia menghantar permintaan untuk memuatkan data dari cakera dan menyimpan hasilnya. Jika jadual waktu telah diminta, ia cepat mengembalikan objek dari ingatan.Terima kasih kepada fungsi mudahnya, kaedah getTrainDepartureTime() tidak perlu diubah hala ke objek asal. Kami hanya menduplikasi fungsinya dalam kaedah baharu.
Jangan buat begini. Jika anda perlu menduplikasi kod atau melakukan sesuatu yang serupa, maka sesuatu telah berlaku, dan anda perlu melihat masalah itu semula dari sudut yang berbeza. Dalam contoh mudah kami, kami tidak mempunyai pilihan lain. Tetapi dalam projek sebenar, kod kemungkinan besar akan ditulis dengan lebih betul.
-
Dalam kod klien, buat objek proksi dan bukannya objek asal:
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]); } } }
Semak
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, ia berfungsi dengan betul.
Anda juga boleh mempertimbangkan pilihan kilang yang mencipta kedua-dua objek asal dan objek proksi, bergantung pada syarat tertentu.
GO TO FULL VERSION