Proxy Design Pattern

Selam Millet!

Proxy Design Pattern ile tasarım desenlerini incelemeye devam ediyoruz! Hiç vakit kaybetmeyelim ve önce problemi anlamaya çalışalım!

Problem

Geliştirdiğiniz bir uygulamada, bellek üzerinde büyük kaynak harcayan bir nesneye ihtiyaç duyduğunuzu varsayalım. Örneğin bu nesne bir resim üzerine işlem yapıyor olabilir. Haliyle böyle maliyetli bir nesneyi her zaman bellekte tutmak istemezsiniz. Sadece istemciden talep geldiği zaman nesneyi oluşturup, metodu çağırıp, işi bittiğinde de nesneyi bellekten kaldıran bir yapıya ihtiyacınız var. İşte bu yapıyı nasıl kuracaksınız?

Biliyorsunuz, problemi anlamak çözümü anlamaktan daha önemli. O nedenle çözüme geçmeden önce, karşımıza çıkma ihtimali olan bir örnek daha vermek isterim. Bu kez uygulamanızın, fiziksel olarak farklı ortamlarda yani istemci-sunucu mekanizmasıyla çalıştığını düşünün. Bu senaryoda, istemcide ihtiyaç duyduğunuz o büyük kaynak harcayan nesne; uzaktaki bir sunucunun belleğinde oluşturuluyor (ki biz bunu web servisi olarak tanımlıyoruz).   Böylesi bir durumda istemci üzerinde öyle bir mimari tasarım yapmalısınız ki yalnızca ilgili servise ihtiyaç duyduğunuzda o nesneyi istemci belleğinde oluşturmalısınız.

İşte gerçek hayat örneğinin gelmesi gereken zaman tam da şimdi! Hepimizin hayatında, tek başımıza halledemeyeceğimiz, öğrenmesi ve tecrübe etmesi oldukça zahmetli işler vardır. Örneğin hukuk ya da muhasebe bu tarz işlerden en çok bilineni. Peki bu işler ile karşılaştığımızda sorunun üzerinden nasıl geliriz? Bir avukat ya da mali müşavir ile anlaşıp işi uzmanına teslim ederiz. Bu uzman kişi de bizim temsilcimiz yani vekilimiz olmuş olur. Sürecin önemli bir kısmı ile vekil ilgilenecektir. Yalnızca gerektiği anlarda biz devreye girer ve bizden istenen işi yaparız.

Çözüm

O zaman nesneler arasındaki problemi de buradaki vekalet ile çözebiliriz. Yani son örnekteki senaryoyu ele alırsak, servis üzerinde oluşan nesneyi istemci tarafında temsil eden bir vekil nesne oluşturabiliriz. İşte Proxy tasarım deseninin de yaptığı tam olarak bu!

Tamamdır sorunu ve bu sorunu nasıl çözeceğimizi ele aldık. Şimdi sıra geldi, bunu kod örneğine dönüştürmeye. Senaryomuzu şöyle kurgulayalım; bir web uygulamasında bulunan ürünlere bir Web API üzerinden erişilmesini ve dönen sonucun istemci tarafından kullanılmasını istiyoruz. Bu noktada hem nesne hem de vekilinin aynı işlemleri yapabilmesi gerekiyor. Tıpkı avukatınızın birebir sizin adınıza işlem yapması gerektiği gibi. O halde hem nesne hem de vekili aynı Interface’i implemete edebilirler. Bu Interface aşağıdaki gibi olsun:

using System.Collections.Generic;
namespace ProxyPattern
{
    public interface IUrunServisi
    {
        List<string> urunAdlariniGetir();
    }
}

Eğer bir  WCF uygulaması geliştirdiyseniz, oradaki ServiceContract’ların da böyle bir interface olduğunu hatırlayacaksınız. Devam edelim.

Gerçek nesnemiz,  IUrunServisi  interface’ini geliştirirken, urunAdlariniGetir()   metodunda istenen operasyonu yapmalı.

public class UrunServisi : IUrunServisi
    {
        public List<string> urunAdlariniGetir()
        {
            //veritabannına bağlanarak ilgili ürünleri aldığımızı ve bu sonucu döndürdüğümüzü varsayın
            return new List<string>() { "Saat", "Gömlek", "Tirbuşon" };
        }
    }

Fakat buradaki UrunServisi  sınıfının nesnesini doğrudan istemciye göndermek yerine, istemcide bu sınıfın bir vekilini oluşturacağız. Yani çağrılması gereken metodu ve bu metot için gerekli olan parametreleri istemciden sunucuya bu vekil nesnemiz gönderecek. Bu durumda istemcide oluşturulacak nesnenin sınıfı aşağıdaki gibi olacak:

public class UrunServisiProxy : IUrunServisi
    {
        public List<string> urunAdlariniGetir()
        {
           
        }
    }

Proxy (tam Türkçesi: Vekil 😊) nesnemiz, adı üzerinde sunucuda üretilmiş olan UrunServisi nesnesini temsil ediyor. Bu durumda UrunServisiProxy nesnesinin tek görevi, gerçek nesneyi çağırarak işlem yapmak olacaktır. O zaman UrunServisiProxy sınıfını aşağıdaki gibi düzenliyorum:

public class UrunServisiProxy : IUrunServisi
    {
        //Dikkat: işi yapacak olan gerçek nesne, bir "alan" olarak tanımlandı:
        private UrunServisi urunServisi = null;
        public List<string> urunAdlariniGetir()
        {
            //proxy nesnemizin bu metodu, gerçek nesnenin aynı metodunu çağıracak: 
            urunServisi = new UrunServisi();
            return urunServisi.urunAdlariniGetir();
        }
    } 

Artık istemcinin erişebileceği, sunucuda oluşacak nesneyi temsil eden sınıfımız hazır. Yani, istemci eğer servisten talepte bulunacaksa önceden belirttiğimiz gibi bu nesne üzerinden bulunacak:

internal class Program
    {
        //Burası client
        private static void Main(string[] args)
        {
            UrunServisiProxy urunServisiProxy = new UrunServisiProxy();
            urunServisiProxy.urunAdlariniGetir();
        }
    }

İşte bu kadar! Açıkça söylemek gerekirse bu tasarım deseninin kod tarafı, yapısal (structural) tasarım desenleri içinde en kolay olanı.

Bu noktada bir şeyi daha belirtmem gerektiğini düşünüyorum. Eğer,  bir web servisini .NET istemcisine Web Reference olarak ekleyip kullanmışsanız, .NET’in …SoapClient kelimeleri ile biten bir sınıf oluşturduğuna dikkat etmişsinizdir. Peki xxxSoapClient sınıfının koduna hiç göz attınız mı? Atmadıysanız ilk fırsatta bakın. Söz konusu sınıf, Web Servis tarafından oluşturulan nesnenin Proxy’sinden başka bir şey değil aslında!

Evet! Proxy tasarım deseninin yazısını da burada tamamlamış olduk dostlar!

Faydalı ve anlaşılır olmuştur umarım.

Görüşmek üzere!

4 thoughts on “Proxy Design Pattern

    1. Yorumun için teşekkürler. Çözümü bir kez daha anlatmaya çalışayım; eğer beğenirsen makaleye de eklerim 🙂
      Proxy’nin tam olarak çözdüğü şey şu: yalnızca gerektiğinde maliyetli nesneyi oluşturmak.
      İstemci kodundan hareket edelim.


      UrunServisiProxy urunServisiProxy = new UrunServisiProxy();
      urunServisiProxy.urunAdlariniGetir();

      istemci, ürün adlarına ihtiyaç duyduğu zaman, proxy nesnesini oluşturuyor ve ilgili metodu çağırıyor.
      Bu metot, gerçek nesneyi kullanacak yani wrap edecek. Peki neden? Burada unutulmaması gereken şey, UrunServisi nesnesinin uzak sunucuda olduğu. Yani aslında bir geri serileştirme (de-serialization) sonucu oluşturuyor. Bu noktada çözümün neden anlamsız gibi gözüktüğünü anlıyorum. Çünkü iç içe sarmalama anlamsız gibi görünüyor.


      public class UrunServisiProxy : IUrunServisi
      {
      private UrunServisi urunServisi = null;
      public List urunAdlariniGetir()
      {
      urunServisi = new UrunServisi();
      return urunServisi.urunAdlariniGetir();
      }
      }

      Aslında, proxy içinde bir denetim mekanizması da eklenebilir. Diyeiim ki tüm istemcilere değil de sadece kayıtlı olanlara bir servis sunacağız. O zaman gerekli denetimi nerede yapmalısınız? Gerçek nesneye ulaşmadan önce, kayıtlı istemci olup olmadığını ancak proxy içinde kontrol edebilirsiniz. Yani:

      public List urunAdlariniGetir()
      {
      //eğer istemci tanınıyorsa, (kullanıcı adı ve şifre kontrolü denetimi yaparak) nesneyi deserialize et:
      urunServisi = new UrunServisi();
      //metodu çağır
      return urunServisi.urunAdlariniGetir();

      }

      Umarım bu kez çözümü anlatabilmişimdir.
      Tekrar dönüş yaparsan sevinirim.

Leave a Reply