CodeGym /Java Blogu /Rastgele /Proxy tasarım deseni
John Squirrels
Seviye
San Francisco

Proxy tasarım deseni

grupta yayınlandı
Programlamada, uygulamanızın mimarisini doğru planlamak önemlidir. Tasarım kalıpları, bunu başarmanın vazgeçilmez bir yoludur. Bugün proxy'ler hakkında konuşalım.

Neden bir proxy'ye ihtiyacınız var?

Bu model, bir nesneye kontrollü erişimle ilgili sorunları çözmeye yardımcı olur. "Neden kontrollü erişime ihtiyacımız var?" diye sorabilirsiniz. Neyin ne olduğunu anlamanıza yardımcı olacak birkaç duruma bakalım.

örnek 1

Bir veritabanından raporları dışa aktarmaktan sorumlu bir sınıfın bulunduğu, bir grup eski kod içeren büyük bir projemiz olduğunu hayal edin. Sınıf senkronize çalışır. Yani veritabanı talebi işlerken tüm sistem boştadır. Bir raporun oluşturulması ortalama olarak 30 dakika sürer. Buna göre, ihracat süreci sabah 12:30'da başlar ve yönetim raporu sabah alır. Bir denetim, raporu normal çalışma saatleri içinde hemen alabilmenin daha iyi olacağını ortaya çıkardı. Başlama zamanı ertelenemez ve sistem veri tabanından yanıt beklerken bloke edemez. Çözüm, sistemin çalışma şeklini değiştirmek, raporu ayrı bir iş parçacığında oluşturmak ve dışa aktarmaktır. Bu çözüm, sistemin her zamanki gibi çalışmasına izin verecek ve yönetim yeni raporlar alacaktır. Fakat, bir sorun var: sistemin diğer bölümleri onun işlevselliğini kullandığından mevcut kod yeniden yazılamıyor. Bu durumda, raporları dışa aktarma isteklerini alacak, başlangıç ​​zamanını günlüğe kaydedecek ve ayrı bir iş parçacığı başlatacak bir ara proxy sınıfını tanıtmak için proxy modelini kullanabiliriz. Rapor oluşturulduğunda, ileti dizisi sonlandırılır ve herkes mutlu olur.

Örnek 2

Bir geliştirme ekibi bir etkinlik web sitesi oluşturuyor. Ekip, yeni etkinliklerle ilgili verileri almak için bir üçüncü taraf hizmetini sorgular. Özel bir özel kitaplık, hizmetle etkileşimi kolaylaştırır. Geliştirme sırasında bir sorun keşfedildi: üçüncü taraf sistem verilerini günde bir kez günceller, ancak bir kullanıcı sayfayı her yenilediğinde ona bir istek gönderilir. Bu, çok sayıda istek oluşturur ve hizmet yanıt vermeyi durdurur. Çözüm, hizmetin yanıtını önbelleğe almak ve önbelleğe alınan sonucu sayfalar yeniden yüklenirken ziyaretçilere döndürerek önbelleği gerektiği gibi güncellemektir. Bu durumda proxy tasarım deseni, mevcut işlevselliği değiştirmeyen mükemmel bir çözümdür.

Tasarım deseninin arkasındaki ilke

Bu modeli uygulamak için bir proxy sınıfı oluşturmanız gerekir. İstemci kodu için davranışını taklit ederek hizmet sınıfının arayüzünü uygular. Bu şekilde, müşteri gerçek nesne yerine bir proxy ile etkileşime girer. Kural olarak, tüm istekler hizmet sınıfına iletilir, ancak öncesinde veya sonrasında ek işlemler yapılır. Basitçe söylemek gerekirse, bir proxy, müşteri kodu ile hedef nesne arasındaki bir katmandır. Eski ve çok yavaş bir sabit diskten sorgu sonuçlarını önbelleğe alma örneğini ele alalım. Diyelim ki, mantığı değiştirilemeyen eski bir uygulamada elektrikli trenler için bir zaman çizelgesinden bahsediyoruz. Her gün sabit bir zamanda güncellenmiş bir zaman çizelgesine sahip bir disk yerleştirilir. Böylece sahibiz:
  1. TrainTimetablearayüz.
  2. ElectricTrainTimetable, bu arayüzü uygulayan.
  3. İstemci kodu, bu sınıf aracılığıyla dosya sistemiyle etkileşime girer.
  4. TimetableDisplaymüşteri sınıfı Yöntemi printTimetable(), sınıfın yöntemlerini kullanır ElectricTrainTimetable.
Diyagram basittir: Proxy tasarım deseni: - 2Şu anda, yöntemin her çağrılmasıyla printTimetable(), ElectricTrainTimetablesınıf diske erişir, verileri yükler ve istemciye sunar. Sistem çalışıyor ama çok yavaş. Sonuç olarak, önbelleğe alma mekanizması eklenerek sistem performansının artırılmasına karar verildi. Bu, proxy modeli kullanılarak yapılabilir: Proxy tasarım deseni: - 3Böylece sınıf, eski sınıf yerine sınıfla TimetableDisplayetkileşime girdiğini bile fark etmez . ElectricTrainTimetableProxyYeni uygulama, zaman çizelgesini günde bir kez yükler. Tekrar istekleri için bellekten önceden yüklenmiş nesneyi döndürür.

Bir proxy için en iyi görevler nelerdir?

İşte bu kalıbın kesinlikle işe yarayacağı birkaç durum:
  1. Önbelleğe almak
  2. Gecikmeli veya yavaş başlatma Bir nesneyi gerektiği gibi yükleyebiliyorsanız neden hemen yükleyesiniz?
  3. Günlüğe kaydetme istekleri
  4. Verilerin ve erişimin ara doğrulaması
  5. Çalışan iş parçacıkları başlatılıyor
  6. Bir nesneye erişimi kaydetme
Ve başka kullanım durumları da var. Bu modelin arkasındaki prensibi anlayarak, başarılı bir şekilde uygulanabileceği durumları belirleyebilirsiniz. İlk bakışta proxy , cephe ile aynı şeyi yapar , ancak durum böyle değildir. Proxy , hizmet nesnesiyle aynı arabirime sahiptir. Ayrıca, bu deseni dekoratör veya Adaptör desenleriyle karıştırmayın . Bir dekoratör , genişletilmiş bir arabirim sağlar ve bir adaptör, alternatif bir arabirim sağlar.

Avantajlar ve dezavantajlar

  • + Hizmet nesnesine erişimi istediğiniz gibi kontrol edebilirsiniz
  • + Hizmet nesnesinin yaşam döngüsünü yönetmeyle ilgili ek yetenekler
  • + Servis nesnesi olmadan çalışır
  • + Performansı ve kod güvenliğini artırır.
  • - Ek istekler nedeniyle performansın kötüleşme riski vardır
  • - Sınıf hiyerarşisini daha karmaşık hale getirir

Pratikte proxy modeli

Bir sabit diskten tren tarifelerini okuyan bir sistem uygulayalım:

public interface TrainTimetable {
   String[] getTimetable();
   String getTrainDepartureTime();
}
İşte ana arayüzü uygulayan sınıf:

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 "";
   }
}
Tren tarifesini her aldığınızda, program diskten bir dosya okur. Ama bu sorunlarımızın sadece başlangıcı. Tek bir tren için bile zaman çizelgesini her aldığınızda tüm dosya okunur! Böyle bir kodun yalnızca yapılmaması gerekenlere ilişkin örneklerde bulunması iyidir :) İstemci sınıfı:

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]);
       }
   }
}
Örnek dosya:

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
Test edelim:

public static void main(String[] args) {
   TimetableDisplay timetableDisplay = new timetableDisplay();
   timetableDisplay.printTimetable();
}
Çıktı:

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
Şimdi modelimizi tanıtmak için gerekli adımları inceleyelim:
  1. Orijinal nesne yerine proxy kullanımına izin veren bir arayüz tanımlayın. Örneğimizde, bu TrainTimetable.

  2. Proxy sınıfını oluşturun. Hizmet nesnesine bir referansı olmalıdır (sınıfta oluşturun veya yapıcıya iletin).

    İşte proxy sınıfımız:

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

    Bu aşamada, sadece orijinal nesneye referansla bir sınıf oluşturuyoruz ve tüm çağrıları ona yönlendiriyoruz.

  3. Proxy sınıfının mantığını uygulayalım. Temel olarak, çağrılar her zaman orijinal nesneye yönlendirilir.

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

    getTimetable()Zaman çizelgesi dizisinin bellekte önbelleğe alınıp alınmadığını kontrol eder . Değilse, verileri diskten yüklemek için bir istek gönderir ve sonucu kaydeder. Zaman çizelgesi zaten talep edilmişse, nesneyi hızlı bir şekilde bellekten döndürür.

    Basit işlevselliği sayesinde getTrainDepartureTime() yönteminin orijinal nesneye yeniden yönlendirilmesi gerekmiyordu. İşlevselliğini yeni bir yöntemde kopyaladık.

    Bunu yapma. Kodu çoğaltmanız veya benzer bir şey yapmanız gerekiyorsa, bir şeyler ters gitti ve soruna tekrar farklı bir açıdan bakmanız gerekiyor. Basit örneğimizde, başka seçeneğimiz yoktu. Ancak gerçek projelerde kod büyük olasılıkla daha doğru yazılacaktır.

  4. İstemci kodunda orijinal nesne yerine bir proxy nesnesi oluşturun:

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

    Kontrol etmek

    
    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
    

    Harika, düzgün çalışıyor.

    Belirli koşullara bağlı olarak hem orijinal nesne hem de proxy nesne oluşturan bir fabrika seçeneğini de düşünebilirsiniz.

Vedalaşmadan önce, işte size yardımcı olacak bir bağlantı

Hepsi bugün için! Derslere dönüp yeni öğrendiklerinizi pratikte denemek fena fikir olmaz :)
Yorumlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION