Proxy Design Pattern Nedir?

Fatih İzgi
Kodcular
Published in
4 min readJul 3, 2022

Design Patterns eğitim serisindeki ilk yazımızda, tasarım desenlerinin “Yazılımcıların sıklıkla karşılaştığı problemlere ve yazılımların doğasında bulunan genel ihtiyaçlara getirilen çözümler” olduğunu söylemiştik. Sık karşılaşılan problemlerden bir tanesi ise; bir nesneye erişimin kontrol altında tutulması ihtiyacıdır. Bu yazımızda, bu problemi çözmek için kullanabileceğimiz Proxy Design Pattern yapısını inceleyeceğiz.

Konuya başlamadan önce problemin ne olduğunu biraz analiz edelim;

Bir şirketteki yönetici, bütün çalışanları ile ilgilenmek durumundadır ve bütün çalışanların da yöneticiden belirli beklentileri vardır. Elbette bu, bütün çalışanların istedikleri zaman yönetici ile iletişime geçebileceği anlamına gelmez. Böyle bir durum, pek çok problemi beraberinde getirecektir. Örneğin yönetici, kendi sorumluluğunda olmayan işler için de çalışanları dinlemek zorunda kalacak ve asıl görevlerini yerine getiremeyecektir. Ayrıca, bir yöneticiye her isteyen kişinin doğrudan ulaşabilmesi güvenlik açısından da uygun değildir. Bu sebeple çalışanlar ve yönetici arasında iletişim sağlayan vekiller bulunur. Çalışanlar bu vekiller ile iletişime geçer, vekiller çalışanların isteklerini değerlendirir ve yönetici ile eğer gerekiyorsa iletişime geçerler. Bu durum yazılım dünyasında da geçerlidir. Bir nesnenin diğer tüm nesneler tarafından doğrudan erişilebilir olması bir takım problemleri beraberinde getirebilir.

Bir nesne, çok önemli sorumlulukları gerçekleştiriyor olabilir. Bu nesneye diğer nesneler tarafından doğrudan erişilmesi güvenlik problemlerine sebebiyet verebilir. Ayrıca, ilgili nesnemizin görevlerini yerine getirmesi oldukça maliyetli de olabilir. Örneğin, çalışmak için çok fazla zamana ihtiyaç duyuyor veya çok fazla bellek tüketiyor olabilir. Bu tarz nesnelerin yalnızca ihtiyaç anında kullanılması gerekmektedir. Aksi taktirde sistem performansı olumsuz etkilenecektir. Güvenlik açısından sakıncalı durumlarda ise bu olumsuzluk, sistemin çökmesine kadar giden problemlere dahi sebep olabilir.

Proxy Design Pattern, bir nesneye olan erişimin kontrol altına alınması amacı ile kullanılır. İstemcilerin direkt olarak ilgili nesneye ulaşması yerine bir vekil aracılığı ile (dolaylı olarak) nesneye ulaşmasını sağlar. Vekil nesne, yalnızca gerekli durumlarda asıl nesne ile iletişime geçer ve ilgili işlemler gerçekleştirilir. Yapısal(Structural) tasarım desenlerinden biridir ve kullanılması ile birlikte sistem performansındaki düşüşlere izin verilmemesi ve potansiyel güvenlik tehditlerinin engellenmesi gibi avantajlar elde edilir.

Öncelikle, problemimizi yazılıma aktarmak adına örnek bir yapı oluşturalım :

Görüldüğü gibi, Employee sınıfını ve bu sınıftan türeyen alt sınıfları oluşturduk. Employee sınıfı içerisine manageEmployee isimli bir metot bulunmaktadır. Bu metot, çalışanların birbirleri ile ilgili işlemler gerçekleştirebilmesini sağlıyor olsun. Bu işlemler; bir projeye çalışan atamak, bir projeden çalışan çıkarmak, bir çalışanın bilgilerini güncellemek, bir çalışanın bilgilerini görüntülemek, bir çalışana görev atamak olsun ve bu işlemlerin gerçekleştirimi için ise belirli kurallar belirleyelim :

1- Yalnızca “Yönetici” pozisyonundaki çalışanlar bir projeye çalışan atayabilir ve bir projeden çalışan çıkartabilir.

2- Yalnızca “İnsan Kaynakları” departmanında çalışan çalışanlar bir çalışanın bilgilerini güncelleyebilir.

3- Yalnızca “İnsan Kaynakları” departmanında çalışan ve “Uzman” olarak çalışan çalışanlar bir çalışanın bilgilerini görüntüleyebilir.

4- Bütün çalışanlar birbirlerine görev atayabilir.

Yukarıdaki kurallara uygun bir yapı oluşturmak için akla gelen ilk çözümlerden bir tanesi yalnızca ilgili sınıflarda ilgili metotları oluşturmak olabilir. Örneğin, İnsan Kaynakları departmanına Bilgi Görüntüle&Güncelle metotları; Yazılım Departmanlarına Projeye Çalışan Ekle/Çıkar metotları tanımlanabilir ve bu metotlar içerisinde kontrol işlemleri yapılabilir. Her ne kadar Inheritance bu noktada bize yardımcı oluyor olsa da ilerleyen süreçte işler karmaşık bir hal alabilir. Örneğin, yeni departmanların eklenmesi veya kuralların güncellenmesi durumunda her defasında ilgili sınıflara gidilerek her metot içerisinde güncelleme yapmak gerekecektir. Dolayısıyla, böyle bir yapı inşa etmek yerine bütün bu işlemleri tek merkezden idare etmek daha mantıklı olacaktır :

Yukarıdaki sınıf sayesinde tüm işlemleri tek bir merkezden yürütme imkanı yakaladık. Artık olası güncellemelerde her sınıfa ayrı ayrı bakım yapmak yerine, yalnızca EmployeeManager içerisinde değişikliğe gidebiliriz. Peki ya bu sınıftan bir nesne üretmek oldukça maliyetli olsaydı? EmployeeManager sınıfının pek çok nesneden meydana geldiğini ve dolayısıyla bu sınıftan bir nesne oluşturmanın oldukça uzun zaman aldığını ve yüksek bellek alanı tükettiğini varsayalım. Her ne kadar kontrolleri tek merkeze toplasak bile nihayetinde bu metotların kullanılması için(tüm metot ve alanları static yapmak oldukça mantıksız olacağından) employeeManager nesnesinin yaratılması gerekmektedir. Bu durum da sistem performansındaki bir düşüşe sebebiyet verecektir.

Nesneye doğrudan erişimin sebep olacağı problemleri engellemek adına bir Interface oluşturmak ve bu Interface’i implement eden iki farklı sınıf oluşturmak mantıklı bir çözüm olabilir. İlk sınıf, gerekli işlemleri gerçekleştiren ve nesne üretimi maliyetli olan sınıftır. İkinci sınıf ise maliyetsiz bir şekilde nesnesi üretilebilen, gerekli kontrolleri yapmakla sorumlu ve yalnızca gerekli durumlarda ilk sınıf ile iletişime geçen Vekil Sınıf’tır. Proxy Design Pattern, bu mantık üzerine kuruludur. Yapıyı inceleyecek olursak :

Görüldüğü üzere ProxyEmployeeManager, işlem gerçekleştirmek isteyen nesne ile ilgili kontroller yapıyor ve eğer gerekmiyorsa RealEmployeeManager sınıfı nesnesini hiç oluşturmuyor. Böylece gereksiz maliyetin önüne geçiyor.

Not : EmployeeManager gibi nesne üretimi maliyetli olan bir sınıfta(herhangi bir sakıncası bulunmuyorsa) Singleton Design Pattern kullanılabilir. Elbette bu kullanım(ana nesnenin durumunda göre) bütün problemleri engellemede yetersiz kalabilir.

Proxy, bahsettiğimiz üzere farklı amaçlarla kullanılan bir yapıdır. Dolayısıyla, kullanım amacına göre de farklı isimlendirmelerini görmek de mümkündür. Sıklıkla karşılaştığımız örnekleri inceleyecek olursak :

Remote Proxy : Kullanılmak istenilen nesne uzaktaki bir sunucuda çalışıyor olabilir. Bu nesneye erişim için bir köprü görevi görür ve doğrudan erişimin potansiyel olumsuz sonuçlarından kurtarır.

Virtual Proxy : Bazı nesneler sürekli olarak bellekte hayatta kalır ve sistemi yoğun olarak kullanır. Uygulama çalıştığı anda bu nesneyi yaratmaktansa, ihtiyaç duyulduğu zamana kadar nesne yaratma işlemini erteler. Sistem performansı için önemli bir yere sahiptir.

Protection Proxy : Ana nesneye erişim iznini kontrol eder ve bu izni yalnızca belirli nesnelere verir. Sistem güvenliği açısından hayati değere sahiptir.

Caching Proxy : Nesnenin çalışması durumunda yüksek bellek tüketen sonuçlar elde edilebilir. İstemcilerin sıklıkla gönderdiği isteklerin sonuçları hep aynı değeri veriyorsa bu sonuçları ön belleğe kaydeder ve böylece nesneye gereksiz erişimi engeller. Sistem performansı için önemli bir yere sahiptir.

TÜM YAPI

Proxy Design Pattern konusunun ayrıntılarını ve inceliklerini öğrendiğimize göre tüm yapıyı incelemeye başlayabiliriz :

Output :

Yetkisiz işlem girişimi! Girişimci : Çalışan1

Yetkisiz işlem girişimi! Girişimci : Çalışan2

Projeye çalışan atama sayfası yükleniyor…

Yetkisiz işlem girişimi! Girişimci : Çalışan4

Yetkisiz işlem girişimi! Girişimci : Çalışan5

Çalışan bilgilerine erişim sayfası yükleniyor…

Yetkisiz işlem girişimi! Girişimci : Çalışan2

Yetkisiz işlem girişimi! Girişimci : Çalışan3

Yetkisiz işlem girişimi! Girişimci : Çalışan4

Yetkisiz işlem girişimi! Girişimci : Çalışan5

Yararlandığım Kaynaklar :

1- Refactoring Guru

2- HowToDoInJava

3- GeeksForGeeks

4- Tutarial Points

--

--