JavaRush /Blog Java /Random-MS /Corak reka bentuk proksi

Corak reka bentuk proksi

Diterbitkan dalam kumpulan
Dalam pengaturcaraan, adalah penting untuk merancang dengan betul seni bina aplikasi. Alat yang sangat diperlukan untuk ini ialah corak reka bentuk. Hari ini kita akan bercakap tentang Proksi, atau dengan kata lain, Timbalan.

Mengapa anda memerlukan Timbalan?

Corak ini membantu menyelesaikan masalah yang berkaitan dengan akses terkawal kepada objek. Anda mungkin mempunyai soalan: "Mengapa kami memerlukan akses terkawal sedemikian?" 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 memuat turun laporan daripada pangkalan data. Kelas berfungsi secara serentak, iaitu, keseluruhan sistem melahu sementara pangkalan data memproses permintaan. Secara purata, laporan dijana dalam masa 30 minit. Disebabkan ciri ini, muat naiknya bermula pada 00:30 dan pihak pengurusan menerima laporan ini pada waktu pagi. Semasa analisis, ternyata perlu menerima laporan segera selepas ia dijana, iaitu, dalam masa sehari. Adalah mustahil untuk menjadualkan semula masa mula, kerana sistem akan menunggu respons daripada pangkalan data. Penyelesaiannya ialah menukar prinsip operasi dengan memulakan penjanaan muat naik dan laporan dalam urutan berasingan. Penyelesaian ini akan membolehkan sistem beroperasi seperti biasa, dan pengurusan akan menerima laporan baharu. Walau bagaimanapun, terdapat masalah: kod semasa tidak boleh ditulis semula, kerana fungsinya digunakan oleh bahagian lain sistem. Dalam kes ini, anda boleh memperkenalkan kelas proksi perantaraan menggunakan corak Timbalan, yang akan menerima permintaan untuk memuat naik laporan, log masa mula dan melancarkan urutan berasingan. Apabila laporan dijana, urutan akan menyelesaikan kerjanya dan semua orang akan gembira.

Contoh 2

Pasukan pembangunan mencipta laman web poster. Untuk mendapatkan data tentang acara baharu, mereka beralih kepada perkhidmatan pihak ketiga, interaksi yang dilaksanakan melalui perpustakaan tertutup khas. Semasa pembangunan, masalah timbul: sistem pihak ketiga mengemas kini data sekali sehari, dan permintaan kepadanya berlaku setiap kali pengguna menyegarkan halaman. Ini menghasilkan sejumlah besar permintaan dan perkhidmatan berhenti bertindak balas. Penyelesaiannya adalah untuk cache respons perkhidmatan dan menyediakan pelawat dengan hasil yang disimpan pada setiap but semula, mengemas kini cache ini mengikut keperluan. Dalam kes ini, menggunakan corak Timbalan adalah penyelesaian yang sangat baik tanpa mengubah fungsi siap.

Bagaimana corak berfungsi

Untuk melaksanakan corak ini, anda perlu membuat kelas proksi. Ia melaksanakan antara muka kelas perkhidmatan, mensimulasikan kelakuannya untuk kod klien. Oleh itu, bukannya objek sebenar, pelanggan berinteraksi dengan proksinya. Biasanya, semua permintaan dihantar ke kelas perkhidmatan, tetapi dengan tindakan tambahan sebelum atau selepas panggilannya. Ringkasnya, objek proksi ini adalah lapisan antara kod klien dan objek sasaran. Mari kita lihat contoh caching permintaan daripada cakera lama yang sangat perlahan. Biarkan ia menjadi jadual kereta api elektrik dalam beberapa aplikasi kuno, yang prinsip operasinya tidak boleh diubah. Cakera dengan jadual yang dikemas kini dimasukkan setiap hari pada masa yang ditetapkan. Jadi kita ada:
  1. Antara muka TimetableTrains.
  2. Kelas TimetableElectricTrainsyang melaksanakan antara muka ini.
  3. Melalui kelas inilah kod klien berinteraksi dengan sistem fail cakera.
  4. Kelas pelanggan DisplayTimetable. Kaedahnya printTimetable()menggunakan kaedah kelas TimetableElectricTrains.
Skim ini mudah: Corak Reka Bentuk Proksi - 2Pada masa ini, setiap kali kaedah dipanggil, printTimetable()kelas TimetableElectricTrainsmengakses cakera, memunggah data dan memberikannya kepada pelanggan. Sistem ini berfungsi dengan baik, tetapi sangat perlahan. Oleh itu, ia telah memutuskan untuk meningkatkan prestasi sistem dengan menambah mekanisme caching. Ini boleh dilakukan menggunakan corak Proksi: Corak Reka Bentuk Proksi - 3Dengan cara ini kelas DisplayTimetabletidak akan perasan bahawa ia berinteraksi dengan kelas TimetableElectricTrainsProxydan bukan dengan yang sebelumnya. Pelaksanaan baharu memuatkan jadual sekali sehari, dan atas permintaan berulang, mengembalikan objek yang telah dimuatkan daripada memori.

Untuk tugas apakah lebih baik menggunakan Proksi?

Berikut adalah beberapa situasi di mana corak ini pasti akan berguna:
  1. Caching.
  2. Pelaksanaan malas juga dikenali sebagai pelaksanaan malas. Mengapa memuatkan objek sekaligus apabila anda boleh memuatkannya mengikut keperluan?
  3. Permintaan pembalakan.
  4. Data sementara dan semakan akses.
  5. Melancarkan benang pemprosesan selari.
  6. Merakam atau mengira sejarah panggilan.
Terdapat juga kes penggunaan lain. Memahami prinsip operasi corak ini, anda sendiri boleh mencari aplikasi yang berjaya untuknya. Pada pandangan pertama, Timbalan melakukan perkara yang sama seperti Facade , tetapi tidak. Proksi mempunyai antara muka yang sama dengan objek perkhidmatan. Juga, jangan mengelirukan corak dengan Penghias atau Penyesuai . Penghias menyediakan antara muka lanjutan, manakala Penyesuai menyediakan satu alternatif.

Kelebihan dan kekurangan

  • + Anda boleh mengawal akses kepada objek perkhidmatan mengikut kehendak anda;
  • + Keupayaan tambahan untuk menguruskan kitaran hayat objek perkhidmatan;
  • + Berfungsi tanpa objek perkhidmatan;
  • + Meningkatkan prestasi dan keselamatan kod.
  • - Terdapat risiko kemerosotan dalam prestasi akibat rawatan tambahan;
  • - Menyukarkan struktur kelas program.

Corak gantian dalam amalan

Mari kita laksanakan sistem dengan anda yang membaca jadual kereta api dari cakera:
public interface TimetableTrains {
   String[] getTimetable();
   String getTrainDepartureTime();
}
Kelas yang melaksanakan antara muka 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 cuba mendapatkan jadual semua kereta api, program membaca fail dari cakera. Tetapi ini masih bunga. Fail itu juga dibaca setiap kali anda perlu mendapatkan jadual untuk hanya satu kereta api! Adalah baik bahawa kod sedemikian hanya wujud dalam contoh yang buruk :) Kelas pelanggan:
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 fail:

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 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 pergi melalui langkah-langkah melaksanakan corak kami:
  1. Tentukan antara muka yang membolehkan anda menggunakan proksi baharu dan bukannya objek asal. Dalam contoh kita ialah TimetableTrains.

  2. Buat kelas proksi. Ia mesti mengandungi rujukan kepada objek perkhidmatan (buat dalam kelas atau lulus dalam pembina);

    Berikut ialah kelas proksi 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 peringkat ini, kami hanya mencipta kelas dengan rujukan kepada objek asal dan menghantar semua panggilan kepadanya.

  3. Kami melaksanakan logik kelas proksi. Pada asasnya panggilan sentiasa diubah hala ke objek asal.

    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;
       }
    }

    Kaedah getTimetable()menyemak sama ada tatasusunan jadual dicache dalam ingatan. Jika tidak, ia mengeluarkan permintaan untuk memuatkan data dari cakera, menyimpan hasilnya. Jika permintaan sudah berjalan, ia akan cepat mengembalikan objek daripada ingatan.

    Terima kasih kepada fungsi mudahnya, kaedah getTrainDepartireTime() tidak perlu diubah hala ke objek asal. Kami hanya menduplikasi fungsinya menjadi kaedah baharu.

    Anda tidak boleh berbuat demikian. Jika anda terpaksa menduplikasi kod atau melakukan manipulasi yang serupa, ini bermakna sesuatu telah berlaku dan anda perlu melihat masalah itu dari sudut yang berbeza. Dalam contoh mudah kami tidak ada cara lain, tetapi dalam projek sebenar, kemungkinan besar, kod itu akan ditulis dengan lebih betul.

  4. Gantikan penciptaan objek asal dalam kod klien dengan objek gantian:

    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]);
           }
       }
    }

    Peperiksaan

    
    Поезд  Откуда  Куда   Время отправления Время прибытия    Время в пути
    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

    Hebat, ia berfungsi dengan betul.

    Anda juga boleh mempertimbangkan kilang yang akan mencipta kedua-dua objek asal dan objek gantian bergantung pada syarat tertentu.

Pautan berguna dan bukannya titik

  1. Artikel yang sangat baik tentang corak dan sedikit tentang "Timbalan"

Itu sahaja untuk hari ini! Alangkah baiknya untuk kembali belajar dan menguji pengetahuan baharu anda dalam amalan :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION