Design Pattern Serisi 3: Pipeline
Temiz ve yalın kod yazmak yazılım dünyasında olmazsa olmaz şeylerin başında geliyor. Bunu sağlamak için bir çok yol var bizde bunlardan birine değineceğiz.
Bu yaklaşım birden fazla yerde kullanılıyor, örneğin ASP.NET Core üzerinde http istekleri bir pipeline (veri hattı) ile işlenmekte ve bu işlenme sırasında aralara iyi bir şekilde müdahale edilebilmektedir. Ayrıca, ML.NET’de bu yaklaşım üzerine kurulmuştur. Benim şahsi kanaatim, bu yapıyı birden fazla şekilde hayata geçirebilirsiniz, o yüzden doğaçlama yapmaktan çekinmeyin. Fazla sözü uzatmadan örneğimize geçelim.
Benim yapacağım örnekte yapı 3 farklı bölümden oluşmaktadır. Bunlardan ilki işlerin türeyeceği interface, işlerin daha kolay anlaşılmasını ve unit test yazarken bize kolaylık sağlayacak IPipeObject
.
İçerisinde bulunan şeylere gelirsek, yapılacak işi Invoke
methodu yapacak.Next
ve NextPipe
ise bir sonraki işe geçmemizde yardımcı olacak. Burada dikkat edilmesi gereken nokta Next
ve Invoke
methodlarının bir state objesi almış olduğu, bu obje sayesinde iki iş (pipe) arasında veri taşınmaktadır.
Bir diğeri ise, bu interface sınıfının bir parçasının implement’e edilmiş halinin bulunduğu PipeObject
.
Bu sınıfta ise, bir sonra ki işi çağırmamızı sağlayacak olan Next
methodunun uygulanışını yapıyoruz. Yukarıda dediğim gibi bu işi birden fazla şekilde yapabiliriz, örneğin Next
methodunu kaldırıp, NextPipe
üzerindende gidilip bir sonraki iş çağrılabilir.
Son ana sınıfımız ise, iş hattımızı başlatacak ve gerekli bilgileri barındıracak sınıf Pipeline
.
Sınıf veri hattının başlangıç ve bitiş noktasını tutmakta, bunlar bizim bir sonraki işi, eklenen işe bağlarken yardımcı olacak. Append
method’u eğer veri hattında hiç bir iş yoksa, veri hattının başlangıcını belirlemekte. Ve eğer, ikinci iş ve sonraki işler ekleniyorsa, kuyruk şeklinde işleri birbirine bağlamaktadır. _pipeEnd
değişkeni sürekli en son eklenen işi işaret etmektedir. Bu şekilde eklenen tüm işler birbirine bağlanmaktadır. Start
methodu ile birlikte de iş hattımızı bir değer ile başlatabilmekteyiz.
Ana hatlar tamamlandıysa, şimdi örnek bir veri hattı oluşturabiliriz. Örneğimiz girilen cümleyi belirli işlemlerden geçirerek ekrana yazdırmak olacak. Bunun için aşağıdaki grafiği inceleyebiliriz.
Aşamalar,
- Küçük harflere çevir
Lower
- Kelimelere böl
Split
- Boşluklardan arındır
IgnoreWhiteSpaces
- Ekrana yazdır
Outputer
Lafı uzatmamak için bu işlerin hepsini tek gist’te göstereceğim,Invoke
methodlarına bakarsak her yapılan iş çok basit ve temiz bir şekilde yapıldığını görebiliriz. Ayrıca,Next
methodu ile birlikte veriyi bir sonraki veri hattına gönderiyoruz. Şimdi kodu çalıştırıp sonuçları görebiliriz.
Sonuç
Tests
Bu kadar gelmişken unit test yazmadan olmaz. Hemen xUnit üzerinden tüm sınıflarımız için testlerimizi yazıyoruz. Burada sadece önemli testleri göstereceğim.
Pipeline sınıfındaki Append
methodu doğru çalışmasını kontrol etmemiz için yukarıdaki test’i yazıyoruz. Önceden de belirttiğim gibi interface bize unit test yazarken yardımcı olacak demiştim, 4 adet IPipeObject
mock’luyoruz. Ve her birini Append
ederek NextPipe
değişkenlerinin birbirlerine ardışık olarak atanıp atanmadıklarını kontrol ediyorum. Buna ilaveten veri hattının başlangıç ve bitiş noktasını da kontrol ediyorum.
Asıl amacımız olan veri hattındaki işlerin testlerini test etmemiz mühim. Veri hattına giren ve çıkan veriyi kolayca kontrol edebilmekteyiz.
Yukarıda tanımlamış olduğumuz PipeObject
lerin her birinin testini aşağıda görebilirsiniz.
Sonuç
Bu yapı ile birlikte daha temiz ve düzenli kod yazabilmenin bir yolunu göstermiş olduk. Yukarıdaki kodların proje haline aşağıdan erişebilirsiniz.