JavaRush /Java Blog /Random-ID /Pola desain proxy

Pola desain proxy

Dipublikasikan di grup Random-ID
Dalam pemrograman, penting untuk merencanakan arsitektur aplikasi dengan benar. Alat yang sangat diperlukan untuk ini adalah pola desain. Hari ini kita akan berbicara tentang Proxy, atau dengan kata lain, Deputi.

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:
  1. Antarmuka TimetableTrains.
  2. Kelas TimetableElectricTrainsyang mengimplementasikan antarmuka ini.
  3. Melalui kelas inilah kode klien berinteraksi dengan sistem file disk.
  4. Kelas klien DisplayTimetable. Metodenya printTimetable()menggunakan metode kelas TimetableElectricTrains.
Skemanya sederhana: Pola Desain Proksi - 2Saat ini, setiap kali suatu metode dipanggil, printTimetable()kelas TimetableElectricTrainsmengakses 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: Pola Desain Proksi - 3Dengan cara ini kelas DisplayTimetabletidak akan menyadari bahwa ia sedang berinteraksi dengan kelas tersebut TimetableElectricTrainsProxydan 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:
  1. cache.
  2. Implementasi yang malas disebut juga dengan implementasi yang malas. Mengapa memuat suatu objek sekaligus ketika Anda dapat memuatnya sesuai kebutuhan?
  3. Permintaan pencatatan.
  4. Pemeriksaan data dan akses sementara.
  5. Meluncurkan thread pemrosesan paralel.
  6. Merekam atau menghitung riwayat panggilan.
Ada kasus penggunaan lainnya juga. Memahami prinsip pengoperasian pola ini, Anda sendiri dapat menemukan penerapannya yang berhasil. Sekilas Deputy melakukan hal yang sama seperti Facade , namun ternyata tidak. Proxy memiliki antarmuka yang sama dengan objek layanan. Selain itu, jangan bingung antara polanya dengan Dekorator atau Adaptor . Dekorator menyediakan antarmuka yang diperluas, sedangkan Adaptor menyediakan antarmuka alternatif.

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:
  1. Tentukan antarmuka yang memungkinkan Anda menggunakan proxy baru, bukan objek aslinya. Dalam contoh kita ini adalah TimetableTrains.

  2. 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.

  3. 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.

  4. 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

  1. Artikel bagus tentang pola dan sedikit tentang "Deputi"

Itu saja untuk hari ini! Alangkah baiknya untuk kembali belajar dan menguji pengetahuan baru Anda dalam praktik :)
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION