Image
Haziran 15 2016 00:00

Template Method Pattern

                         Sıralı ya da belirli bir amaca yönelik yapılacak olan işlemlere ait kalıpların oluşturularak, bunların class hiyerarşisinde yukarda olan soyutlanmış bir sınıfta tanımlanması ve bu soyut sınıftan türeyen alt sınıfların bu kalıba uygun davranmasını gerekli kılan tasarım desenidir.
Bu yapı sayesinde, 

  • Alt sınıflarda oluşacak kod tekrarının önüne geçilmiş oluyor,  
  • Olası bir değişiklikte, bu değişikliğin tek merkezden yapılabilinmesi sağlanıyor.   
  • Soyutlanmış sınıfta kurguladığınız algoritmanın değiştirilememesi garanti altına alınıyor
  • Alt sınıfların, üst sınıftaki kalıbın dışına çıkmadan kendi operasyonlarını yapabilmesine olanak sağlanıyor.
  • Bu yapıyı kullanan geliştiricinin özelleştirme yapmasına imkân tanınıyor.

Ne zaman kullanmak gerekir?

  • İki yada daha fazla class aynı algoritma yada iş akışına sahipse
  • İş akışının değişmez olduğu durumlarda, alt classların bu kalıp dışına çıkmadan özelleştirme yapmaları gerektiği durumlarda
  • İş akışlarında / sıralı işlemlerde, bazı adımlar üst sınıf tarafından encapsule edilmiş olabilir. Kurumsal mimari yapılarında genelde frameworkler oluşturulur ve bu frameworkü kullanacak olan geliştiricilerin eğer gerekirse override ederek kodu özelleştirebilmesi istenirse.

Nasıl kullanmalı?

  • Client-lar sadece Üst clastan türemiş alt sınıfları çağırmalı. 
  • Alt sınıflar yapacakları işlere göre değişiklik yapmalı
  • Open/Closed kuralına bağlı kalınmalı

Örnek;
Sipariş Sevk eden bir iş yapısı oluşturalım. Bu yapıda siparişi sevk edecek bir üst (base) clasımız olacak, adına da OrderShipment diyelim. 
Bu base clasta birkaç işlem yapacağız

  1. Siparişe ait bir sevk adresi var mı? (Sipariş nereye gönderilecek )
  2. Siparişi gönderenin client-in Kargo firması ile ilgili işlemleri yapacağı bir TemplateMethodumuz olacak. (Hangi kargo firması ile çalışıyorsa onun belki web service yada api noktasını çağırıp sevk emri verecek)
  3. Sevk edilen bu siparişin çıktısını alacağız

Base /Üst class:

Trolloc

public abstract class OrderShipment
    {
        public string ShippingAddress { get; set; }
 
        public void Ship()
        {
            VerifyShippingData();
            GetShippingLabelFromCarrier();
            PrintLabel();
        }

        public virtual void VerifyShippingData()
        {
            if (string.IsNullOrEmpty(ShippingAddress))
            {
                throw new ApplicationException("Sevk adresi bulunamadı");
            }
        }
        /// <summary>
        ///     Template method-umuz 
        /// </summary>
        public abstract void GetShippingLabelFromCarrier();

        public virtual void PrintLabel()
        {
            Console.WriteLine(ShippingAddress);
        }
    }

Clasımız da, yukarıda belirttiğim gibi 3 metodumuz bulunmakta.  
VerifyShippingData() ile Sevk edilecek adresi kontrol ediyoruz. 
GetShippingLabelFromCarrier() ile bu kalıbı kullanacak olan geliştiricinin istediği gibi özelleştirme yapmasına olanak sağlıyoruz. Kaç tane kargo firması ile anlaşıldı ise o kadar OrderShipment -i implemenre eden class yazıp kullanabilir
PrintLabel() ile sevk bilgilerini yazdırıyoruz.

Ups, Yurtiçi ve Aras kargo ile çalıştığımızı varsayalım. Bu kargo firmaları için kalıbı uygularsak.

    public class UpsOrderShipment : OrderShipment
    {
        public override void GetShippingLabelFromCarrier()
        {
            // UPS web servisi / Api si çağrılıp sevk emri veriliyor
            ShippingAddress = String.Format("UPS:[{0}]", ShippingAddress);
        }
    }

    public class YurtIcıExOrderShipment : OrderShipment
    {
        public override void GetShippingLabelFromCarrier()
        {
            // Yurt içi Kargo web servisi / Api si çağrılıp sevk emri veriliyor
            ShippingAddress = String.Format("Yurt ici :[{0}]", ShippingAddress);
        }
    }

    public class ArasOrderShipment : OrderShipment
    {
        public override void GetShippingLabelFromCarrier()
        {
            // Aras Kargo web servisi / Api si çağrılıp sevk emri veriliyor
            ShippingAddress = String.Format("Aras :[{0}]", ShippingAddress);
        }
    }

Görüldüğü gibi her kargo firması için bir Base classı implemente eden birer class yazdık ve o kargo firması ile ilgili işlemleri Base-deki kalıbı bozmadan uyguladık.
İlerde MNG kargo ilede anlaştığımızı düşünürsek Base classa yada başka bir yere dokunmadan herhangi bir değişiklik yapmadan MNG Kargo için yazacağımız bir class ile işlemi tamamlayabiliriz.
Böylece SRP yide bozmamış oluyoruz.

Örneğimizi çalıştıralım

static void Main(string[] args)
        {
            Console.WriteLine("UPS ile gönderilecek");
            var upsOrder = new UpsOrderShipment
            {
                ShippingAddress = "Büyükdere Caddesi no  Mecidiyeköy istanbul"
            };
            upsOrder.Ship();
            Console.WriteLine("UPS ile gönderildi");

            Console.WriteLine("-----------------------------------------------------");

            Console.WriteLine("Aras Kargo ile gönderilecek");
            var arasOrder = new ArasOrderShipment
            {
                ShippingAddress = "Davutpaşa kampüs d blok... istanbul"
            };
            arasOrder.Ship();
            Console.WriteLine("Aras Kargo ile gönderildi");

        }

 

UPS ve Aras Kargo için 2 örnek oluşturduk. Değerler girdik. Çıktı ise;

Trolloc

 

Sonraki yazıda görüşmek üzere