Visitor Design Pattern
Behavioral tasarım kalıplarına ait olan visitor tasarım kalıbı aslında ismi gibi tam bir ziyaretçi görevi görüyor. Mantığına geçmeden önce literatür tanımını yapalım. Visitor design pattern sınıflara, sınıfların içerisinde değişiklik yapmadan fonksiyonellik ekleme imkanı sunan patterndir.
Peki ismiyle yaptığı işin benzerliği nedir? Evinize bir ziyaretçi yani visitor geldiğini düşünün bazı işlerde size yardım ediyor ve gidiyor. Aslında sizin evin bir üyesi değil ama gelip bir iş yapıyor oturuyor ve sonra kalkıp gidiyor. Visitor desing patterni de aynı bu şekilde iş yapar. Visitor classın içerisindeki fonksiyonellik aslında, esas ana classa ait bir fonksiyonellik değildir. Siz visitor classı ana classa inject edersiniz. İnject edili olduğu sürece visitor class kattığı fonksiyonelliği gerçekleştirir. Gün gelip onun yerine başka bir fonksiyonellik için başka bir visitor inject ettiğinizde eski visitorun kattığı fonksiyonellik gider ve yerine yeni visitorun kattığı fonkiyonellik gelir.
Hemen bir örnek ile ne anlatmaya çalıştığımızı gösterelim.
public interface IStore { void Visit(IVisitor visitor); }
- IStore’u impelemente eden Car classımız Visit metodu üzerinden inject edilen visitor nesnesi ile o anki visitorun kattığı fonksiyonelliği çalıştırabilecek durumdadır.
public class Car : IStore { public string CarName { get; set; } public decimal Price { get; set; } public string CarType { get; set; } public void Visit(IVisitor visitor) { visitor.Accept(this); } }
- IStore’u impelemente eden Bike classımız Visit metodu üzerinden inject edilen visitor nesnesi ile o anki visitorun kattığı fonksiyonelliği çalıştırabilecek durumdadır.
public class Bike : IStore { public string BikeName { get; set; } public decimal Price { get; set; } public string BikeType { get; set; } public void Visit(IVisitor visitor) { visitor.Accept(this); } }
- IVisitorClassı ise her bir concrete class için inject edileceği yerde kullanılmak üzere Accept metodu bulundurmakta.
public interface IVisitor { void Accept(Car car); void Accept(Bike bike); }
- PriceVisitor classı concrete visitor classıdır. İçerisinde Car ve Bike classları için fiyat hesaplama fonksiyonelliği bulundurur. Bu fonksiyonellikler Car ve Bike classları için çalışıyor olsa da bu classların bizzat kendi fonksiyonelliği olmayıp visitor üzerinden çalıştıracaktır.
public class PriceVisitor : IVisitor { private const int CAR_DISCOUNT = 5; private const int BIKE_DISCOUNT = 2; public void Accept(Car car) { decimal carPriceAfterDicount = car.Price - ((car.Price / 100) * CAR_DISCOUNT); Console.WriteLine("Car {0} price: {1}", car.CarName, carPriceAfterDicount); } public void Accept(Bike bike) { decimal bikePriceAfterDicount = bike.Price - ((bike.Price / 100) * BIKE_DISCOUNT); Console.WriteLine("Bike {0} price: {1}", bike.BikeName, bikePriceAfterDicount); } }
- PriceVisitor classı concrete visitor classıdır. İçerisinde Car ve Bike classları için ağırlık hesaplama fonksiyonelliği bulundurur. Bu fonksiyonellikler Car ve Bike classları için çalışıyor olsa da bu classların bizzat kendi fonksiyonelliği olmayıp visitor üzerinden çalıştıracaktır.
public class WeightVisitor : IVisitor { public void Accept(Car car) { switch (car.CarType) { case "Mercedes": Console.WriteLine("Car {0} weight: {1} KG", car.CarName, 1750); break; case "Normal": Console.WriteLine("Car {0} weight: {1} KG", car.CarName, 750); break; } } public void Accept(Bike bike) { switch (bike.BikeType) { case "Bullet": Console.WriteLine("Bike {0} weight: {1} KG", bike.BikeName, 300); break; case "Normal": Console.WriteLine("Bike {0} weight: {1} KG", bike.BikeName, 100); break; } } }
- Aşağıda Car classından iki nesne ve Bike classından da iki nesne yaratılmıştır. Bu nesnelere visit metotları üzerinden ilk olarak priceVisitor inject edilip çalıştırılmıştır. Arkasından da weightVisitor. Görüldüğü gibi bu iki fonksiyonellik de classların kendi fonksiyonelliği olmadığı halde bir visitor class üzerinden bir misafir gibi classlara inject edilip fonksiyonelliklerini çalıştırıp ziyaretin kısası makbuldur deyip geri çekilmişlerdir. Tekrar bu fonksiyonellikler çalıştırılmak istenirse tekrar visitorlerin inject edilmesi gerekir. 🙂
static void Main(string[] args) { List<IStore> store = new List<IStore>(); IStore car1= new Car() { CarName = "A1", Price = 200M, CarType = "Mercedes" }; IStore car2 = new Car() { CarName = "A2", Price = 100M, CarType = "Normal" }; IStore bike1 = new Bike() { BikeName = "B1", Price = 50M, BikeType = "Bullet" }; IStore bike2 = new Bike() { BikeName = "B2", Price = 30M, BikeType = "Normal" }; PriceVisitor priceVisitor = new PriceVisitor(); car1.Visit(priceVisitor); car2.Visit(priceVisitor); bike1.Visit(priceVisitor); bike2.Visit(priceVisitor); WeightVisitor weightVisitor = new WeightVisitor(); car1.Visit(weightVisitor); car2.Visit(weightVisitor); bike1.Visit(weightVisitor); bike2.Visit(weightVisitor); Console.ReadLine(); }