C# Decorator Design Pattern(Decorator Tasarım Deseni)
Merhaba,
Bu içeriğimizde mevcudiyette var olan bir class’a mimarisel yeni bir nitelik kazandırmamızı sağlayacak olan, Structural Pattern(Yapısal Desenler)’lerden Decorator Design Pattern(Decorator Tasarım Deseni) üzerine konuşuyor olacağız.
Yazılım mimarilerinde temel esas, mümkün mertebe birbirlerinden soyutlanmış yapıların gevşek bağlılığı(loose coupling) sağlayarak ortaya koyduğu geliştirmeye dayanmaktadır. Bu geliştirme sürecinde var olan sınıflarımızdaki bir metodun niteliğini değiştirme ihtiyacı hissedildiği zaman bu ihtiyacı kodu bozmaksızın, dinamik bir şekilde giderebilmemiz gerekmektedir. İşte böyle bir durumda, kurulmuş düzeni bozmadan, mimarisel yapılanmanın temellerini oynatmadan bir metodun niteliğini genişletmek için Decorator tasarım deseninden faydalanabiliriz.
İlk olarak genel bir yanlış anlaşılmadan başlamak istiyorum. Decorator pattern’inin genellikle bir class’a yeni bir özellik/member/metot vs. eklemeye yaradığı düşünülmektedir. Halbuki Decorator pattern, hali hazırda var olan bir sınıfın mevcudiyetteki metodunun işlevini genişletmek için vardır.
Decorator pattern, bir class’a metot eklemek için değil, var olan bir sınıfın mevcudiyetteki metodunun işlevini genişletmek için kullanılan bir desendir.
Bu durumu içeriğimizin seyrinde masaya yatıracağımız senaryolar eşliğinde zaten örneklendireceğiz. Şimdi hangi durumlarda bu desene ihtiyaç duyduğumuzu daha da açalım.
Decorator Deseni Ne Zaman Kullanılır?
– Şuayip abi : Yazılım nedir müdür?
– Sokrates : Yazılım değişimdir Şuayipciğim…
Evet… Yazılım nedir? sorusuna verilebilecek en güzel cevap sanırım değişim olacaktır. Bir probleme sunulan çözümün, bazen kâh problemin değişmesinden bazen de kâh problemden öte ihtiyaçların değişmesinden dolayı sürekli güncellenmesi gerekebilmektedir. Hatta, her ne kadar iyi bir mimarisel tasarım gerçekleştirirsek gerçekleştirelim, yeni doğacak ihtiyaçlara binaen yapılması gerekecek olan değişiklikler bizleri yıldırabilecek seviyede olabilmekte ve hatta mimarisel yaklaşımlardan uzaklaştırıp, spagetti çözümlere bile sevkedebilmektedir.
Esasında bu durumun, doğal bir sürecin önüne geçilemeyen bir parçası olduğunun farkında olunması gerekir. İş hayatında geliştirilen yazılımların seyrini; ihtiyaçlar, şartlar, değişken durumlar ve son kullanıcıların beklentisi belirlediği süreci bu değişiklik olgusu her daim hayatımızın bir parçası olacaktır. Bizlere düşen bu olguyla karşılaştığımızda onu paniğe kapılmaksızın karşılayabilmek ve evrensel bir çözümle uygulamanın yeni ihtiyaçlara olan adaptasyonunu sağlayabilmektir.
Şimdi olayı biraz daha teknik boyuta indirgeyerek devam edelim. Geliştirilen yazılımlarda mevcut bir işlemi üstlenen sınıflar ve o sınıflar içerisinde operasyonları yürüten metotlar oluşturulduktan sonra ilgili metotların işlevselliğine dair olan beklentinin artması oldukça doğaldır. Süreçte metotların satır sayıları gittikçe uzayabilir, son halleri ilk hallerinden oldukça alakasız bir sorumluluğa doğru evrilebilir.
Genelde yazılımcılar, oluşturdukları tasarımlarına yeni eklemelerin gelmesiyle tek sorumluluk prensibine tamamen aykırı çözümler üretirler. Nihayetinde sistem artık yeni özellikler barındırmak ve var olan metotlarda işlevselliklerini daha da arttırmak isteyecektir. Böyle bir durumda çözüm olarak sistemin arayüzlerine yeni imzalar ekleyerek gelecek özellikleri karşılayabilmekte mümkündür lakin bu sefer de arayüzleri şişirmenin sonucunda arayüz ayrım prensibine aykırı hareket edilmiş olacaktır. Hem aynı arayüzden türeyen kimi nesneler için yeni özellikler istenirken, kimisi içinde istenmeyebilir. Bu durumda arayüzü genişletmek dummy code’a da sebebiyet verebilecektir, ihtimaldir.
İşte bu ahvalde sistemi bozmadan ve prensiplere aykırı olmadan mimarideki metotların niteliklerini arttırabilmek ve sistemi genişletebilmek için daha efektif bir stratejiye ihtiyaç vardır…
Hali hazırdaki bir sistemin genişlemesi gerektiği durumlarda Decorator deseni biçilmiş kaftandır.
Decorator Deseni Nasıl Çalışır?
Decorator deseni, bir interface’de imzası tanımlanmış herhangi bir metodun işlevini daha da genişletebilmek için ilgili interface’den türeyen bir sınıftan ibarettir. Bu sınıf, ilgili interface’den türediği gibi biryandan da genişleşme operasyonunu yapacağı metodun bulunduğu sınıfı karşılayabilmek için ilgili sınıfa ait bir bağımlılık yani referans barındırır. Buradaki tasarımı anlamlandırabilmek için yukarıdaki diyagram bize yardımcı olacaktır;
- Component
Genişletilecek ve yeni özellikler eklenecek olan metodun imzasını barındıran interface’e karşılık gelmektedir. Bir başka deyişle sistem arayüzüdür. - ConcreteComponent
Sistem arayüzünü gerçekleyen/implemente eden somut sınıflardan birisidir. - Operation
Genişletilecek ve yeni özellikler eklenecek olan metodun ta kendisidir. - Decorator
Sistemin arayüzünü uygulayan ve içerisinde ‘ConcreteComponent’a erişim sağlayabilmesi için sistem arayüzünün bir referansını barındıran sınıftır. - ConcreteDecoratorA&ConcreteDecoratorB
Decorator sınıfından türetilen sınıflardır. Sistemdeki Operation metoduna eklemeyi düşündüğümüz yenilikleri/nitelikleri barındırır.
Bu stratejide; ‘Decorator’, ‘ConcreteComponent’te genişletilecek olan ‘Operation’ metodunu ‘Component’ arayüzü sayesinde kendisine uygulayacak ve kendisinden türeyen ‘ConcreteDecoratorA’ ve ‘ConcreteDecoratorB’ sınıflarına override edilebilir bir vaziyette yani virtual olarak kalıtsal aktaracaktır. Ayrıca barındıracağı ‘Component’ referansı sayesinde üzerinde değişiklik yapılacak ‘ConcreteComponent’ nesnesini de alt decorator’lere gönderecektir. İşte bu sınıflarda yapılması beklenen tüm değişiklikler, yenilikler, geliştirmeler ve davranışlar ilgili metoda uygulanacaktır. Diyagrama göz atarsanız eğer; ‘ConcreteDecoratorA’ dekoratöründe ‘ConcreteComponent’ nesnesindeki ‘Operation’ fonksiyonunun önceliğine ‘addedState’ niteliğini eklemekte, ‘ConcreteDecoratorB’de ise ilgili fonksiyonun yine önceliğine ‘addedState’ ile birlikte sonrasına ‘AddedBehavior’ nitelikleri eklenmektedir. Böylece her iki decorator’de beklenen değişiklikler gerçekleştirilmiş ve ilgili metot genişletilmiş olmaktadır.
Decorator deseni ihtiyaca binaen hem sistemi genişletmekte hem de tek sorumluluk prensibi ile arayüz ayrım prensibi eşliğinde soruna çözüm getirmektedir.
Örnek Senaryolar
Şimdi Decorator tasarım desenini üç farklı senaryo üzerinden örneklendirelim;
Senaryo 1
Bu senaryomuzda Repository tasarım deseni üzerinden seyredeceğiz. Uygulamada hali hazırda geliştirilmiş olan repository deseni üzerinden veritabanı işlemlerinin başarıyla gerçekleştirildiğini ve ihtiyaca binaen ilgili tasarım ile tüm sorguları tıkır tıkır ürettiğimizi ve istediğimiz veriyi istediğimiz modelde elde edebildiğimizi düşünelim… Her şey yolunda… Her şey mükemmel… derkeennnn, zaman gelip çatar ve kaçınılmaz gerçekle yüzleşilir! İş birimindeki geliştiricilerden gelen istekler kapıyı çalar ve aşağıdaki değişiklik talepleriyle döşenmiş raylar boşa çıkar…
- Select işleminden önce güvenlik kontrolü yapılsın ardından select işlemi gerçekleştirilsin,
- Herhangi bir kayıt eklendiğinde, silindiğinde yahut güncellendiğinde işlemden sonra gerekli loglar tutulsun,
- Herhangi bir kayıt silindiğinde veya güncellendiğinde CRM veritabanına API’lar aracılığıyla bağlanılarak aynı değişiklikler orayada yansıtılsın,
- Herhangi bir kayıt güncellendiğinde kim tarafından hangi tarihte yapıldığına dair yöneticiye mail gönderilsin.
Herşeyi tam teferruatıyla tamamladığınızı düşündüğünüz repository tasarımınızın üzerine böyle bir istek yağmuru geldiğini düşünürseniz eğer muhtemelen birçoğunuz isyan edecektir. Hele hele iş birimlerinden gelen bu tarz talepler, sanki kurulan düzene karşı bir direnç oluşturmak ve biz yazılım geliştiricilerine sırf iş çıkarmak için kasıtlı bir örgüt misali hareket ediyormuşcasına da algılanması yanında cabası olacaktır.
Evet… Bu tarz istekler onca iş onca tasarımın temellerini oynatacak mahiyette olabilmekte ve bundan dolayı psikolojik gerginliklere bile sebebiyet verebilmektedir. Hatta haklı olabileceğinizi düşünerekten ‘ulan başta söyleseydiniz ya!’ şeklinde tepkiler bile verebilirsiniz. Amma velakin bu durumda pekte haklı olacağınız kanaatinde değilim. Nihayetinde yukarıdaki satırlarda değindiğimiz gibi yazılım sürecinde değişiklik esastır ve bir yazılımcı olarak bu değişikliklere her daim hazır ve açık olmanız gerekmektedir.
Dolayısıyla böyle bir durumun verdiği tansiyon tüm mimariyi alt üst edecek endişesine kapılmanızı sağlayabilir ve korkuyla, kara kara düşünmenize bile sebebiyet verebilir… Aklınızdan if-else ile mi çözsek? ya da farklı bir repository’mi tasarlasak gibi fikirleri geçiriyor olabilir lakin sonrasında iş birimlerinden gelebilecek sonraki istek ihtimallerini düşünerek, ilgili isteklerin şu tabloda olsun, bu tabloda olmasın gibisinden saçma sapan detaylıları olabileceği ihtimalinden dolayı vaz geçip iyice karamsarlığa kapılabilirsiniz. …Ulan ne güzelde kodlamıştım, şimdi nereden çıktı bu!…
Hayır! Korkmayın… İşte tamda böyle bir ahvalde, var olan repository deseni üzerinde istenilen değişiklikleri mimari temellerine zerre dokunmaksızın gerçekleştirebilmek ve iş birimlerine sunabilmek için Decorator deseni biçilmiş kaftan misali yetişecek ve probleme çözüm getirecektir.
Şimdi gelin örneklendirmede kullanacağımız repository’nin ilk başlardaki kodunu inceleyelim.
class Repository<T> : IRepository<T> where T : class { public T Get(int id) { Console.WriteLine("Id bazlı veri çekildi."); return null; } public T GetAll() { Console.WriteLine("Tüm veriler çekildi."); return null; } public void Add(T model) { Console.WriteLine("Model eklendi."); } public void Delete(T model) { Console.WriteLine("Model silindi."); } public void Update(T model) { Console.WriteLine("Model güncellendi."); } }
Başlarda bu şekilde tasarlanmış olan ve süreçte iş birimlerinden gelen istekler neticesinde değişiklik gerektirecek metotları barındıran repository sınıfının kodu yukarıdaki gibidir. Decorator tasarım deseni açısından bu sınıf ‘ConcreteComponent’a karşılık gelmektedir. Yani işlevini genişletmek gereken metotları barındıran sınıfa…
Bizler şimdi decorator deseni sayesinde bu sınıf içerisindeki ‘Select’, ‘Insert’, ‘Update’ ve ‘Delete’ sorgularını oluşturan ilgili metotlara istenilen değişiklikleri uygulatacak ve gerekli operasyonları gerçekleştireceğiz. Bunun için ilk olarak tasarımdaki ‘Decorator’ sınıfını oluşturarak başlayalım.
//Decorator class DecoratorRepository<T> : IRepository<T> where T : class { readonly IRepository<T> _repository; public DecoratorRepository(IRepository<T> repository) { _repository = repository; } virtual public void Add(T model) { _repository.Add(model); } virtual public void Delete(T model) { _repository.Delete(model); } virtual public T Get(int id) { return _repository.Get(id); } virtual public T GetAll() { return _repository.GetAll(); } virtual public void Update(T model) { _repository.Update(model); } }
Decorator deseninde ‘Component’a karşılık gelen ‘IRepository’ interface’inden türeyen ‘DecoratorRepository’ isimli ‘Decorator’ sınıfımızı oluşturmuş bulunmaktayız. Dikkat ederseniz bu sınıf ‘Component’tan yani ‘IRepository’den türemekte ve içerisinde genişletme uygulanacak olan ‘ConcreteComponent’ nesnesini referans edebilmesi için ‘IRepository’ referansı barındırmaktadır. Bir başka öncemli husus ise ‘IRepository’ arayüzü ile uygulatılan metotlar virtual olarak işaretlenmiştir. Bunun nedeni bu ‘Decorator’ sınıfından asıl değişikliklerin sorumluluğunu üstlenecek olan ‘ConcreteDecorator’ sınıfları türetilecek ve kalıtımsal aktarılan bu metotların içeriğinde ana işlevselliklerinin yanında, öncesinde ya da sonrasında istenilen değişiklikleri uygulayabilmek için ezilmesi(override) gerekecektir.
Şimdi gelin istekleri yerine getiren ‘ConcereDecorator’ sınıflarını sırasıyla oluşturalım.
Select işleminden önce güvenlik kontrolü yapılsın ardından select işlemi gerçekleştirilsin
//ConcreteDecorator class SecurityRepositoryDecorator<T> : DecoratorRepository<T> where T : class { readonly IRepository<T> _repository; public SecurityRepositoryDecorator(IRepository<T> repository) : base(repository) { _repository = repository; } public override T Get(int id) { Console.WriteLine("Güvenlik kontrolü yapılıyor..."); return base.Get(id); } public override T GetAll() { Console.WriteLine("Güvenlik kontrolü yapılıyor..."); return base.GetAll(); } }
İş biriminden gelen ilk istek üzerine ‘SecurityRepositoryDecorator’ sınıfı oluşturulmuştur. Burada kontrolün sadece select işlemine odaklı istenilmesinden dolayı ‘Get’ ve ‘GetAll’ metotları override edilmiş ve gerekli çalışma gerçekleştirilmiştir.
Herhangi bir kayıt eklendiğinde, silindiğinde yahut güncellendiğinde işlemden sonra gerekli loglar tutulsun
//ConcreteDecorator class LoggingRepositoryDecorator<T> : DecoratorRepository<T> where T : class { readonly IRepository<T> _repository; public LoggingRepositoryDecorator(IRepository<T> repository) : base(repository) { _repository = repository; } public override void Add(T model) { base.Add(model); Console.WriteLine($"LOG : {typeof(T).Name} eklenmiştir."); } public override void Delete(T model) { base.Delete(model); Console.WriteLine($"LOG : {typeof(T).Name} silinmiştir."); } public override void Update(T model) { base.Update(model); Console.WriteLine($"LOG : {typeof(T).Name} güncellenmiştir."); } }
İkinci istekte ekleme, silme ve güncelleme işlemlerinden sonra log tutulması istenildiği için ‘Add’, ‘Delete’ ve ‘Update’ fonksiyonları override edilmekte ve işlevsel olarak loglar ilgili operasyonlardan sonra tutulmaktadır.
Herhangi bir kayıt silindiğinde veya güncellendiğinde CRM veritabanına API’lar aracılığıyla bağlanılarak aynı değişiklikler oraya da yansıtılsın
//ConcreteDecorator class SendCRMRepositoryDecorator<T> : DecoratorRepository<T> where T : class { readonly IRepository<T> _repository; public SendCRMRepositoryDecorator(IRepository<T> repository) : base(repository) { _repository = repository; } public override void Delete(T model) { base.Delete(model); Console.WriteLine("Kaydın silinmesi CRM veritabanına işlendi."); } public override void Update(T model) { base.Update(model); Console.WriteLine("Kaydın güncellenmesi CRM veritabanına işlendi."); } }
Üçüncü istekte silme ve güncelleme durumlarında CRM veritabanı ile etkileşim kurulması istendiği için ‘Delete’ ve ‘Update’ sorguları override edilmekte ve işlemlerden sonra gerekli operasyonlar gerçekleştirilmektedir.
Herhangi bir kayıt güncellendiğinde kim tarafından hangi tarihte yapıldığına dair yöneticiye mail gönderilsin
//ConcreteDecorator class SendMailRepositoryDecorator<T> : DecoratorRepository<T> where T : class { readonly IRepository<T> _repository; public SendMailRepositoryDecorator(IRepository<T> repository) : base(repository) { _repository = repository; } public override void Update(T model) { base.Update(model); Console.WriteLine($"{DateTime.Now} | Yöneticiye mail gönderildi..."); } }
Sonuncu istekte ise sadece güncelleme durumunda mail gönderilmesi istenildiği için ‘Update’ override edilmiş ve ardından gerekli operasyonlar gerçekleştirilmiştir.
Evet… Görüldüğü üzere geliştirdiğimiz ‘ConcreteDecorator’ sınıfları sayesinde herhangi bir değişikliği çok rahat mimariye dahil edebilmekteyiz. Böylece bu isteklerin dışında bambaşka istekler gelirse eğer mimari temellerine dokunmadan rahatlıkla karşılayabilmekteyiz.
Şimdi gelin ürettiğimiz bu yapıyı kullanalım;
class Program { static void Main(string[] args) { Console.WriteLine("Repository"); Repository<Employee> repository = new Repository<Employee>(); repository.Get(3); repository.GetAll(); repository.Add(new Employee()); repository.Delete(new Employee()); repository.Update(new Employee()); Console.WriteLine("\nSecurityRepositoryDecorator"); Console.WriteLine("****************"); SecurityRepositoryDecorator<Employee> securityRepositoryDecorator = new SecurityRepositoryDecorator<Employee>(repository); securityRepositoryDecorator.Get(3); securityRepositoryDecorator.GetAll(); securityRepositoryDecorator.Add(new Employee()); securityRepositoryDecorator.Delete(new Employee()); securityRepositoryDecorator.Update(new Employee()); Console.WriteLine("\nLoggingRepositoryDecorator"); Console.WriteLine("****************"); LoggingRepositoryDecorator<Employee> loggingRepositoryDecorator = new LoggingRepositoryDecorator<Employee>(repository); loggingRepositoryDecorator.Get(3); loggingRepositoryDecorator.GetAll(); loggingRepositoryDecorator.Add(new Employee()); loggingRepositoryDecorator.Delete(new Employee()); loggingRepositoryDecorator.Update(new Employee()); Console.WriteLine("\nSendCRMRepositoryDecorator"); Console.WriteLine("****************"); SendCRMRepositoryDecorator<Employee> sendCRMRepositoryDecorator = new SendCRMRepositoryDecorator<Employee>(repository); sendCRMRepositoryDecorator.Get(3); sendCRMRepositoryDecorator.GetAll(); sendCRMRepositoryDecorator.Add(new Employee()); sendCRMRepositoryDecorator.Delete(new Employee()); sendCRMRepositoryDecorator.Update(new Employee()); Console.WriteLine("\nSendMailRepositoryDecorator"); Console.WriteLine("****************"); SendMailRepositoryDecorator<Employee> sendMailRepositoryDecorator = new SendMailRepositoryDecorator<Employee>(repository); sendMailRepositoryDecorator.Get(3); sendMailRepositoryDecorator.GetAll(); sendMailRepositoryDecorator.Add(new Employee()); sendMailRepositoryDecorator.Delete(new Employee()); sendMailRepositoryDecorator.Update(new Employee()); } }
Uygulamayı derleyip, çalıştıralım…
Ve sonuç… Görüldüğü üzere süreçte gelen istekler neticesinde metotlarımızın mahiyetini mevcut mimarinin temellerine dokunmaksızın, hızlı ve esnek bir şekilde geliştirmiş bulunmaktayız. Önceden de ifade edildiği gibi bu desen sayesinde gelecek tüm istekler farketmeksizin bir ‘ConcreteDecorator’ tarafından karşılanabilecek ve hızlıca mimariye dahil edilebilecektir.
Şimdi kurguyu daha net oturtabilmek için bir başka senaryo üzerinden daha örneklendirme yapalım.
Senaryo 2
Bu senaryoda baş rol oyuncumuz sistemdeki bir daire çizici sınıf olacaktır. Bu sınıf, ilk etapta aldığı boyut ve konum değerleri eşliğinde daire çizme işlevselliğini göstermektedir.//Component interface IShape { void Draw(Size size, int location); }
//ConcreteComponent class Circle : IShape { public void Draw(Size size, int location) { Console.WriteLine("Daire çizildi."); } }
Yukarıdaki gibi tasarlanmış olan sınıf üzerinde aşağıdaki yeni gereksinimlere ihtiyaç duyulmaktadır;
- Sadece dairenin kenarları renklendirilebilsin,
- Sadece daireyi içi renklendirilebilsin,
- Yanları ve iç kısmı aynı anda renklendirilebilsin.
Bu gereksinimleri normal şartlarda ‘Circle’ sınıfını güncelleyerek karşılayabiliriz. Lakin bizim buradaki amacımız decorator deseninin tatbiki olduğu için ilgili desen merkezli bir çözüm sergileyeceğiz.
İlk olarak ‘Decorator’ sınıfını oluşturalım.
//Decorator class Decorator : IShape { readonly IShape _shape; public Decorator(IShape shape) { _shape = shape; } virtual public void Draw(Size size, int location) { _shape.Draw(size, location); } }
Ardından tüm yenilikleri kazandıracak olan ‘ConcreteDecorator’ sınıflarını oluşturalım.
Sadece dairenin kenarları renklendirilebilsin
//ConcreteDecorator class CircleEdge : Decorator { readonly IShape _shape; public CircleEdge(IShape shape) : base(shape) { _shape = shape; } private void ColorEdge() { Console.WriteLine("Kenarlar renklendirildi."); } public override void Draw(Size size, int location) { base.Draw(size, location); ColorEdge(); } }
İlk istekte olduğu gibi ‘Draw’ fonksiyonuna sadece kenarları renklendiren işlevsellik kazandırılmıştır.
Sadece daireyi içi renklendirilebilsin
//ConcreteDecorator class CircleFill : Decorator { readonly IShape _shape; public CircleFill(IShape shape) : base(shape) { _shape = shape; } private void ColorFill() { Console.WriteLine("Daire içi renklendirildi."); } public override void Draw(Size size, int location) { base.Draw(size, location); ColorFill(); } }
İkinci istekte olduğu gibi ‘Draw’ fonksiyonuna sadece daire içini renklendiren işlevsellik kazandırılmıştır.
Yanları ve iç kısmı aynı anda renklendirilebilsin
//ConcreteDecorator class CircleFull : Decorator { readonly IShape _shape; public CircleFull(IShape shape) : base(shape) { _shape = shape; } private void ColorEdge() { Console.WriteLine("Kenarlar renklendirildi."); } private void ColorFill() { Console.WriteLine("Daire içi renklendirildi."); } public override void Draw(Size size, int location) { base.Draw(size, location); ColorEdge(); ColorFill(); } }
Üçüncü istekte olduğu gibi ‘Draw’ fonksiyonuna kenar ve daire içi olmak üzere her iki renklendirme işlevide gerçekleştirilmiştir.
Evet… Geliştirilen ‘ConcreteDecorator’ler sayesinde yenilikler uygulanmış ve mimariye kazandırılmıştır. Şimdi bu geliştirilen yenilikleri kullanalım;
class Program { static void Main(string[] args) { Console.WriteLine("Circle"); Circle circle = new Circle(); circle.Draw(new Size(5, 10), 100); Console.WriteLine("\nCircleEdge"); Console.WriteLine("****************"); CircleEdge circleEdge = new CircleEdge(circle); circleEdge.Draw(new Size(5, 10), 100); Console.WriteLine("\nCircleFill"); Console.WriteLine("****************"); CircleFill circleFill = new CircleFill(circle); circleFill.Draw(new Size(5, 10), 100); Console.WriteLine("\nCircleFull"); Console.WriteLine("****************"); CircleFull circleFull = new CircleFull(circle); circleFull.Draw(new Size(5, 10), 100); } }
Uygulamayı derleyip, çalıştıralım.
İşte sonuç… Yenilikleri sisteme uyarlayabilmek harika sizin içinde harika olsa gerek 🙂
Ve son senaryo üzerinden konuyu toparlayalım…
Senaryo 3
Bu senaryo Ağ Veri İletim modelleri üzerine kurgulanmış olacaktır. Öncelikle ağ veri iletim modeli hakkında ufak bir bilgi vererek başlayalım. Yandaki şekilde uygulama katmanı(Application) olan FTP’den fiziksel katman(Physical Layer) olan ethernet driver’a gerçekleştirilen veri akışlarında kullanılan TCP yahut UDP dosya aktarım protokolleri ele alınmaktadır.Normal şartlarda uygulama katmanı, transport katmanını atlayıp doğrudan veri aktarımı için ağ katmanını(Network) kullanabilse de(bknz : ICMP) genellikle transport katmanı olarak TCP ya da UDP kullanılmaktadır. Bu durumu kod üzerine yansıtırsak eğer;
//Component interface IDatagram { void Send(); }
//ConcreteComponent class AppDatagram : IDatagram { public void Send() { Console.WriteLine("IP datagram gönder."); } }
Görüldüğü üzere sadece Network katmanının kullanıldığı bir durum olduğunu varsayalım. Lakin böyle bir durumda ihtiyaca binaen TCP yahut UDP’nin kullanılması söz konusu olursa decorator desenine başvurup, duruma çözüm getirelim. Haliyle ‘Decorator’ sınıfını oluşturarak başlayalım;
//Decorator class TransportLayer : IDatagram { readonly IDatagram _datagram; public TransportLayer(IDatagram datagram) { _datagram = datagram; } virtual public void Send() { _datagram.Send(); } }
‘Decorator’ sınıfından sonra ilk olarak TCP protokolüne uygun ‘ConcreteDecorator’ sınıfını tasarlayalım;
//ConcreteDecorator class UseTCP : TransportLayer { public UseTCP(IDatagram datagram) : base(datagram) { } private void AddTCPHeader() { Console.WriteLine("TCP protokolü devreye sokuldu."); } public override void Send() { AddTCPHeader(); base.Send(); } }
Ardından UDP protokolü için ‘ConcreteDecorator’ oluşturalım;
//ConcreteDecorator class UseUDP : TransportLayer { public UseUDP(IDatagram datagram) : base(datagram) { } private void AddUDPHeader() { Console.WriteLine("UDP protokolü devreye sokuldu."); } public override void Send() { AddUDPHeader(); base.Send(); } }
Evet… İlgili farklar ‘ConcreteDecorator’ sınıflarına yansıtılmış oldu. Şimdi geliştirilen yapıları kullanma vakti;
class Program { static void Main(string[] args) { Console.WriteLine("AppDatagram"); AppDatagram appDatagram = new AppDatagram(); appDatagram.Send(); Console.WriteLine("\nUseTCP"); Console.WriteLine("****************"); UseTCP useTCP = new UseTCP(appDatagram); useTCP.Send(); Console.WriteLine("\nUseUDP"); Console.WriteLine("****************"); UseUDP useUDP = new UseUDP(appDatagram); useUDP.Send(); } }
Hemen akabinde uygulamayı derleyip, çalıştıralım. Ve aşağıdaki gibi sonucu gözlemleyelim…
Nihai olarak
Decorator deseninin herhangi bir nesnenin geliştirilmiş bir versiyonunu elde etmek ve metotlardaki davranışları değiştirmek için kullanılan strateji olduğunu üç farklı senaryoda detaylıca gözlemlemiş bulunmaktayız. Bu ve bunun gibi akıl ve tecrübe ürünü desenler sayesinde, karşımıza çıkacak olan problemlere yaklaşımımız her daim daha emin ve daha net duruşlu olmaktadır. O yüzdendir ki, son zamanlarda içlerinde boğulduğumuz framework’ler ve programlama dilleri arasında arada kafamızı kaldırıp tasarım desenleri üzerine hasbihallerde bulunmak bizlere bir programcı olduğumuzu ve daha da ötesi yaptığımız işin kod dilinde edebiyattan ibaret olduğunu hatırlatacağı kanaatindeyim…
Okuduğunuz için teşekkür ederim…
İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…
Not : Örnek projeleri indirmek için buraya tıklayınız.
Harika
Detaylı ve temiz bir anlatım. Emeğinize sağlık.
Te$ekkurler 🙂
hocam poliymorphism’in modern hali diyebilir miyiz
Elinize sağlık hocam. Senaryolar çok iyi 🙂