Interface Segregation Principle

SOLID prensipleri yazı dizisinde sırada SOLID'in "I" olan Interface Segregation (ISP) var. Bu prensip bize kısaca şunu söylüyor; "Nesneler asla ihtiyacı olmayan property/metot vs içeren interface'leri implement etmeye zorlanmamalıdır  !".

Terminolojide bu interface'ler "fat" yada "polluted" interfaces diye adlandırılır.  ISP uygulanmadığında bir den fazla sorumluluğu olan nesneler ortaya çıkar ki bu da aslında SOLID'in "S" si olan "Single Responsibility" prensibine aykırı bir şeydir. Bu sınıflar yüklenen çoklu sorumluluklardan dolayı zaman içerisinde yönetilemez hale gelirler ve projemiz patates olur çıkar. Bu tarz case'ler le karşılaşıldığında interface içerisinde bulunan kullanılmaması gereken özellikleri içeren yeni interface'ler tanımlanır ve ihtiyaca göre nesneler tarafından implement edilir. Eğer projeniz bu prensibi ihlal ediyorsa Adapter Design Pattern avantajlarını kullanarak da ilerleyebilirsiniz. İlgili nesne interface'i implement edip hiç kullanmayacağı metotlara sahip olduğunda class'ınız içerisinde "NotImplementedException" yazan metotlar olacaktır ve buda OOP açısından hiç istenen bir şey değildir.

ISP'yi örnek bir case üzerinde anlatmaya çalışalım. Bir tane FileLog ve DbLog yapan işlemleri yapan bir proje geliştireceğiz ve içerisinde ILog adında bir interface tanımlayalım ve bu interface'inde Log(), OpenConn(), CloseConn() gibi metodları olsun.

public interface ILog
{ 
    void Log(string message);

    void OpenConnection();

    void CloseConnection();
}

 

İlk olarak DBLogger sınıfını yazalım

public class DBLogger : ILog
{		
        public void Log(string message)
        {
            //Code to log data to a database
        }

        public void OpenConnection()
        {
            //Opens database connection
        }

        public void CloseConnection()
        {
           //Closes the database connection
        }
}

 

Şimdi de FileLogger class'ını yazalım. 

public class FileLogger : ILog
    {
        public void Log(string message)
        {
            //Code to log to a file           
        }
    }

FileLog işlemi yaparken Db de olduğu gibi bir Connection açıp kapama işlemi yok ve bu metotları FileLogger'da kullanmak istemiyoruz sadece ILog interface'inde tanımlı olan Log(string message) metodunu kullanmak istiyoruz. Projeyi derlediğinizde şöyle bir hata alırsınız "FileLogger class doesn't implement the interface members ILog.OpenConnection() and ILog.OpenConnection()" .  Peki hatayı görüp eksik interface'in eksik üyelerini implement edelim. Bu sefer class'ımız aşağıdaki gibi olacaktır.

public class FileLogger : ILog
    {
        public void Log(string message)
        {
             //Code to log to a file           
        }

        public void CloseConnection()
        {
            throw new NotImplementedException();
        }

        public void OpenConnection()
        {
            throw new NotImplementedException();
        }
    }

E şimdi ne oldu ? Patates ! Hiç ihtiyacımız olmasa da FileLogger class'ına 2 tane gereksiz metot kazandırdık. İşte ISP burada devreye giriyor. Yapmamız gereken şey yeni interface veya interface'ler tanımlayarak FileLogger ve DbLogger class'larının sadece ihtiyacı olan metotları içeren interface'i implemente etmesini sağlamak.

Yeni interface'ler aşağıdaki gibi olacaktır.

    public interface ILog
    {
        void Log(string message);
    }

    public interface IDBLog: ILog
    {
        void OpenConnection();

        void CloseConnection();
    }

    public interface IFileLog: ILog
    {
        void CheckFileSize();

        void GenerateFileName();
    }

 

Bu interface'leri implement eden DbLogger ve FileLogger class'larımızda aşağıdaki gibidir.

 public class FileLogger : IFileLog
    {
        public void CheckFileSize()
        {
            //Code to check log file size
        }

        public void GenerateFileName()
        {
            //Code to generate a new file name
        }
		
        public void Log(string message)
        {
            //Code to log data to the log file
        }
    }

    public class DBLogger : IDBLog
    {
        public void Log(string message)
        {
            //Code to log data to the database
        }

        public void OpenConnection()
        {
            //Code to open database connection
        }

         public void CloseConnection()
         {
            //Code to close database connection
         }
    }

Interface Segregation çok önemli bir prensiptir. Özellikle Adapter Design Pattern ile haşır neşir olan arkadaşlar için dahada fazla öneme sahiptir. İçerisinde 60-70 tane üyesi bulunan interface'ler yazmaktan da çekinin bu gibi durumlarda bir yolunu bulup interface üyelerini ayırıp gruplayıp yeni interface'ler türetmeye çalışmamız daa doğru olacaktır. 

C# 7.0 Yenilikleri

Daha C# 6.0 'ın tüm özelliklerini yeni yeni kavramışken Microsoft C# 7.0 ile gelecek olan özelliklerden bazılarını açıkladı bile. Gelecek olan feature'lara baktığımızda çokta fazla major yenilikler yok gibi ancak yine de kayda değer şeyler var. Gelin bu feauture'ları ufaktan bi inceleyelim

Öncelikle C#7.0 ın özellikleri VS2015'de default olarak yok. Normalde vs2015'de yeni bir proje açtığınızda default C# 6.0 ile gelir. 7.0'ın özelliklerini aktifleştirmek için VS de proje oluştururken ufak bir kaç şey yapmak gerekiyor.

İlk olarak C#da bir tane console proje oluşturalım ve sonrasında solution'da bulunan projeye sağ tıklayıp özellikler diyelim. Açılan ekrandan Build tabına geçelim ve "conditional compilation symbols" textbox'ına __DEMO__ yazalım. Projeyi build ettikten sonra artık C# 7.0 özelliklerini otomatik olarak alacaktır.

 

 

C# 7.0 Features

  • Local functions
  • Binary literals
  • Digit separators
  • Pattern matching
  • Ref returns and locals

 

Local functions

Bir çok yazılım dili fonksiyon içinde fonksiyon yazımına izin verirken C# için bu geçerli değildi. 7.0 ile artık fonksiyon içerisine fonksiyon tanımlıyor olacağız.

public int Method_1(int x,int y)
	{
		int Metho_2(int x)
		{
			return x*x;
		}
	 
		return Metho_2(x)*y;
	}

 

Binary literals and Digit separators

Bu özelliğin aslında 6.0 ile geleceği söyleniyordu ancak galiba yetiştiremediler ki 7.0 ile geliyor. Bu özellikle birlikte artık numeric tanımlamaları binary olarak yazabileceğiz. 

	public void BinaryLiterals()
	{
		var numbers = new[] { 0b0, 0b1, 0b10 };
		foreach (var number in numbers)
		{
		  Console.WriteLine(number);
		}
	}

 

Daha büyük sayıları daha kolay okuyabilmek için ise "_" ayıracını kullanarak digitleri gruplayabiliriz ve bu decimal, hexa veya binary'ler içinde geçerlidir. 

int oneBillion = 1_000_000_000;
int num_1 = 0x7FFF_1234;
int num_2 = 0b1001_0110_1010_0101;

 

Pattern matching

Pattern matching özelliği fonkysionel programlama dillerinde çok yaygın bir özellik ve C# 7.0 da "is" operatörü ile bazı özellikleri bize sunuyor ancak final sürümü ile birlikte daha fazla özellik ekleneceği söyleniyor.

1) Type Pattern

Type pattern reference typle'ların runtime type test işlemi yaparken işimize yarayacak olan bir özellik.

public void Foo(object item)
{
    if (item is string s)
    {
        WriteLine(s.Length);
    }
}

2) Constant Pattern

Constant pattern bir ifadenin runtime değerini test etmek için kullanılır.

public void Foo(object item)
{
    switch (item)
    {
        case 10:
            WriteLine("It's ten");
            break;
        default:
            WriteLine("It's something else");
            break;
    }
}

3) Var Pattern

public void Foo(object item)
 {
     if(item is var x)
     {
         WriteLine(item == x); // prints true
     }
 }

4) Wildcard Pattern

public void Foo(object item)
 {
     if(item is *)
     {
         WriteLine("Hi there"); //will be executed
     }
 }

5) Recursive Pattern

public int Sum(LinkedListNode<int> root)
{
    switch (root)
    {
        case null: return 0;
        case LinkedListNode<int> { Value is var head, Next is var tail }:
            return head + Sum(tail);
        case *: return 0;
    }
}

 

Ref returns and locals

C# ın ilk versiyonundan itibaren "ref" keyword'ünü kullanarak metotlara pass by reference ile parametreler geçebiliyoruz. Bu özellikle birlikte metottan pass by reference ile değer dönmesini sağlıyor.

static void Main()
 {
     var arr = new[] { 1, 2, 3, 4 };

     ref int item = ref Get(arr, 1);

     Console.WriteLine(item); //2

     item = 10;

     Console.WriteLine(arr[1]); //10

     Console.ReadLine();
 }

ref int Get(int[] array, int index)
{
   return ref array[index]; 
}

 

Kısaca C# 7.0 ile gelecek olan feature'lara değindik ancak relase olması için henüz çok erken ve ilerleyen sürümlerde üstte belirttiğimiz özelliklerinin bazıları değişebilir de. 7.0 ile ilgili yeni duyumlar geldikçe de yazmaya devam...

 

Aspect Oriented Programming Nedir

Aspect Oriented Programming (AOP) tükçesi "Cephe Yönelimli Programlama" bir programlama paradigmasıdır. İsim olarak bazılarımıza yabancı geliyor olabilir çünkü çok yeni bir kavram değil ve gelişen yazılım teknolojileri ve AOP nin daha kolay ve verimli implement edilmesini sağlayacak "PostSharp" gibi tool'ların çıkmasıyla birlikte epey bir önemli hale gelir oldu AOP.

Biz yazılımcılar daha iyi kodlar yazmak için hep kullandığımız bir cümle var "Separation of Concern". AOP'nin çıkış noktası aslında buna dayanıyor diyebiliriz. AOP birbiriyle kesişen ilgilerin (Cross-Cutting Concerns) ayrılması üzerinedir. Uygulama genelinde kullanılacak olan yapıları (logging,exception hand., cache, etc.) core tarafta yazdığımız koddan ayırarak bir çeşit ayrı küçük programcıklar şeklinde yazıp projede kullanmayı hedefler diyebiliriz.

Örnek olarak 70.000 kişinin çalıştığı çok büyük bir holding için uygulama geliştiriyoruz geriye bütün çalışan listesini dönen bir metod yazıyor olalım ve klasik her uygulamada olması gereken belli başlı şeyler vardır; Cache,ExceptionHandling, Logging gibi bizde metodumuzda bunları yapıyor olalım;

 public IEnumerable<Employee> GetEmployeeList()
        {
			//Request'i yapan kişinin yetkisi varmı yokmu kontrol et
			//metoda girerken request'i log'la
			
            try
            {
                var resultList = DbQuery("Select * from Employee"); // database de ki tabloya sorgu attığımız varsayalım ve 70 bin kayıt gelsin
				
                //geriye dönen sonuçları cache'e at bir sonrakine cache'den ver

                return resultList;
            }
            catch (Exception ex)
            {
                // meydana gelen Exception'ı handle edip log'la ve client'a gidecek olan response'u modify et  
                throw;
            }
			
			//metoddan çıkarken response'u log'la
        }
}

Yukarıda bulunan metodu incelediğimizde ne kadar eksik olduğunu görebiliyoruz. Yorum satırlarında yazan işlemler için geliştirmeler yapmamız gerekmekte ancak bu geliştirmeyi nasıl yapacağız  ? CheckUserAuth(), LogRequest(), LogException(), LogResponse(), ModifyResponse() gibi metodlar yazıp bu metodları ilgili yerlerde her metodda yazmak herhalde ilk akla gelen çözüm ancak AOP bize daha farklı şekilde yapmamız gerektiğini söylüyor. Bunları ayrı modüller olarak tasarlayıp daha kullanılabilir, okunabilir ve SOLID prensiplerine uygun geliştirmeler yapmamız gerektiğini söyler.

Peki birbirleri ile çakışan ilgileri birbirlerinden nasıl ayıracağız ? İşte bu noktada karşımıza interceptor çıkmakta.

Interceptor

Interceptor’ belirli noktalarda metot çağrımları sırasında araya girerek çakışan ilgilerimizi işletmemizi ve yönetmemizi sağlamakta. Buda metotların çalışmasından önce veya sonra bir takım işlemleri gerçekleştirebilmemeizi sağlar ve AOP nin yapısı tamamiyle bunun üzerine kurulu desek yanlış olmaz heralde. Interceptor'u implemente etme olayına girmicem çünkü yukarıda da bahsettiğim gibi .Net tarafında Nuget üzerinden indirip kullanabileceğimiz Postsharp kütüphanesi bu işi diplerine kadar yapmakta ve bizlere sadece attribute tanımlamaları yapmayı bırakmakta. 

Şimdi yukarıda yazmış olduğumuz kodu gelin birde AOP standartlarına uygun şekilde yazalım.

        [UserAuthAspect]
        [LoggingAspect]
        [AppCacheAspect(25000)]
        [ExceptionAspect]
        public IEnumerable<Employee> GetEmployeeList()
        {
            var resultList = DbQuery("Select * from Employee");
            return resultList;
        }

[UserAuthAspect] [LoggingAspect] [AppCacheAspect] [ExceptionAspect] attribute'lerini tanımladık ve AOP nin dediği gibi Cross-Cutting yani kesişen yerleri Aspect'ler kullanarak attribute seviyesinde kullanılabilir hale getirdik.Yazmış olduğumuz 2. metot ile 1. metot arasındaki satır sayısı farkına baktığımızda dağlar kadar fark var ve en önemlisi daha okunabilir bir kod yazmış olduk.  

Aspect-Oriented Programming'in Sağladıkları

  1. İçi içe yazılmış ve sürekli tekrar eden kodlardan kurtulabiliyoruz,
  2. Daha temiz ve anlaşılır kodlar yazabiliyoruz,
  3. Yazmış olduğumuz kodları daha abstract hale getirerek modülerliğini arttırıyoruz,
  4. Bakım ve geliştirme maliyetlerini azaltıyoruz,
  5. Uygulamamızı daha yönetilebilir ve daha esnek hale getirebiliyoruz.

Görüldüğü üzre AOP yaklaşımı geliştirdiğimiz uygulamalar için bizlere bir çok faydalar sunmakta ve Postsharp gibi çeşitli tool'lar ile birlikte projenize AOP'ye uygun hale getirmek dahada kolay hale gelmiş durumda. Bundan sonraki AOP ile ilgili yazılarda Postsharp kullanarak Cache, Logging, ExceptionHandling gibi örnekler ile deva ediyor olacağız.  

Egosuz Programlama & Programcı Egosu

Biz developer'lar bazen yaptığımız iş gereği "dünyayı kurtarıyoruz" havasına girebiliyoruz (aslında sadece developer'lar değil test müh, analistler, tasarımcı arkadaşlar vs.). Sektörde her zaman bu gibi arkadaşlar hep varlar ve dehşet-ül vahşet düzeyde bir özgüven-ego patlaması yaşayabiliyorlar.Bu arkadaşlar istisnai olmadığı taktirde %99 oranında hep yazdıkları kodların veya ortaya attıkları tezlerin en iyisi olduğunu iddia ederler ve genelde "abi bundan daha iyi bir çözüm yok yaa.." gibi cümlelerle tanırız bu arkadaşları. Neden bu şekilde iddialı cümleler kurduklarını anlamak için bence biraz gerilere gidip bu arkadaşların staj dönemlerine inmek gerekir zira bu arkadaşlar çok büyük olasılıkla sağlam bir code review'lık dönemini kaçırmış olabilirler veya junior'lık dönemlerinde kodları inşaata kaçmış olabilir :)

Gereksiz ve anlamsızca ego sahibi olan bu arkadaşlar her şey hakkında en iyi ve en kabul görebilir öneriler sunduklarını düşünürler, her ortamda teknik bilgilerini değişik bir jargonla anlatarak ortaya koymaya çalışırlar, en iyi kodu onlar yazmıştır ve eleştiriye kapalıdırlar. Bazen öyle durumlar olur ki bir konu hakkında konuşuluyordur ve hiçbir fikri olmamasına rağmen sırf o konu hakkında expert olduğunu kanıtlamaya çalışmak adına tuhaf bir şekilde konuşmaya başlarlar. Tabiki de benim fikrim ancak bu arkadaşlarla kavgasız gürültüsüz iletişim kurabilmenin en iyi yolu bu tarz hareketler yaptıklarını anladığınızda öyle bir şey söyleyin ki bir daha benzeri hareketler yapmaya kalkışmasın. Bu tabii ki de küfretmek falan değil :) Ortaya koydukları tez'in sırf söylemiş olmak için söylediklerini düşünüyorsanız ve siz daha doğru bir tez'e sahipseniz bu tezinizi savunmaktan kesinlikle çekinmeyin ve onun tezini çürütmeye çalışın.

Böyle bir kişi eğer ekip arkadaşınızsa yani aynı projede beraber development yapıyorsanız şu gibi cümleler duymanız hiç zor değil "abi burayı böyle yazmasak daha iyi yaa..", "abi ben daha önce bunla ilgili çok kral bi çözüm bulmuştum yaa..","bu böylemi yapılır yaa.." falanlar filanlar. Bu gibi cümleler kuran bir ekip arkadaşınız var ise ve yüksek oranda ego sahibi olduğunu düşünüyorsanız benzer cümleler kurmasının asıl amacı üstünlük sağlamaya çalışmak veya benzer bir kodu oda yazmıştır zamanında ve o yazdığının ilelebet en iyi çözüm olacağını düşünür çünkü ezberlemiştir onun için hep bildiği yoldan gitmek gerekmektedir ve senide ısrarla o yola sokmaya çalışır.

Evet malesef bende bu arkadaşlardan biriyle bir süreliğine çalışmak zorunda kaldım ve yaşadığım bir örneği vermeden geçemicem. Eski çalıştığım şirkette bir mobil projesini geliştirme yapan arkadaştan devraldım ve geliştirmeye ben devam edecektim kendisi başka bir  departmana geçmişti. Projeyi devraldığımda markette bulunan versiyonu yanılmıyorsam 1.1 di ve ben yaklaşık 2 sene o projede çalışmıştım. 2 sene dolmasına yakın bir dün bu arkadaş burnu 5 karış kalkık vaziyette yanıma geldi ve ne hikmettir bilmem proje hakkında sorular sormaya başladı.Sorulardan biride uygulamanın bir sonraki versiyonunun ne olacağıydı. Bende 3.0 olacağını söyledim.O arkadaş sahip olduğun egoyla şöyle bir cümle kullandı "Ben sana bişey söylimmi siz bu versiyonlamayı kesin yanlış yapıyorsunuz..." bende dedim ki "wtf..? yani ne alaka nerden çıkardın bunu ?" oda "ben bu projeyi bıraktığımda v1.1 di.." arkadaş zannediyordu ki kendisi uygulamanın %25 inin geliştirmesini yapıp v1.1 ile markete çıkıp uygulama bitmişti. Geriye kalan 2 sene içerisinde uygulamanın %85'e yakının geliştirilip 6 defa update çıkıldığından haberi yok, haberi olmasa daha düşünmesine de gerek yok çünkü 2 sene önce o uygulamayı yaptı ve bitti. Ben naptım peki ? Bir güzel o kişiye konuyu açıklayıp arkasını dönüp fıtı fıtı gidişini izleyerek çayımı yudumladım.

Malesef ego sahibi insanlar IT sektöründe hep olacaktır, bu insanlar yarın bir gün bizlerde olabiliriz ama sahip olduğumuz ego'nun makul düzeyde olması çok önemli.

Peki gerçekten biz developer'lar makul düzeyde ego'ya nasıl sahip olabiliriz, neler yapmalıyız ..? Bununla ilgili bilgisayar bilimleri için bir çok makale yayınlamış olan yazar Gerard M. Weinberg "Egoless Programming" adında bir makale yazıyor. Üstad konu ile ilgili yaklaşık 40 yıl önce 10 tane kural tanımlamış. Bunlar;

1. Understand and accept that you will make mistakes

Türkçesi "herkes gibi sende hata yapacaksın arkadaş bunu anla ve kabul et.." diyor. Bug'ı olmayan uygulama olmaz ve her zaman uygulamalarda exception'lar meydana gelecektir. Önemli olan bu gibi durumlar production'ı etkilemeden yakalayıp müdahale edip uygulamamızı hatalardan arındırmaktır. Tabi bug'ları ortaya çıkartmak içinde çok iyi test süreçleri gerekmektedir.

 

2. You are not your code

Review yapmanın asıl amacının meydana gelmiş veya gelecek olan problemleri bulmak olduğun hatırla ve problemler olacaktır da. Review sırasında birisi projenizde herhangi bir problem bulduğunda veya eleştirel bir yorumda bulunduğunda bunu kişisel bir şey olarak algılama ve sadece geliştirme yapmaya devam et.

 

3. No matter how much you know, someone else will always know more

Hiç kimse ben iyi programcıyım dememelidir. Her zaman için herkesin farklı bakış açısından sorunları analiz edip çözümler ürettiğini düşünmek gerekir ve buda her zaman için daha iyisinin olabileceği gerçeğini unutmamamız gerektiğini işaret eder. Sahip olduğumuz bilgi ve beceriler tabiki çok değerli ancak bunun da bir limitinin olduğunu bilmemiz gerekir. Geliştirme yaparken her developer'ın izlediği farklı bir yol olabilir ve daha önce senin hiç bilmediğin-denemediğin bir şekilde sorunları çözüyor olabilir ve bu gibi case'leri çok fazla abartıp sidik yarışına girmeye gerek yoktur. Sadece şunu bilmek gerekir her zaman senden daha farklı veya senden daha iyisini bilen biri olacaktır.

 

4. Don’t rewrite code without consultation

Bu kural bize kodu fix etme ile yeniden yazma arasında ince bir çizgi olduğundan bahsediyor. Bu farkı iyi bir şekilde bil ve değişiklik yapacağın yerlerde core koda dokunup projeyi yeniden geliştirmene neden olacak şeylerden uzak dur.

 

5. Treat people who know less than you with respect, deference, and patience

Ekibinde veya etrafında her zaman senin kadar teknik bilgisi olmayan insanlar olacaktır. Bunlara karşı anlayışlı ve saygılı olmaya çalış. Eğer mümkünse onlara konudan bahset ve teknik tarafını anlamada yardımcı ol. 

 

6. The only constant in the world is change

Değişim yazılım dünyasında kaçınılmazdır ve değişime açık ol ve geldiğinde gülümseyerek kabul et :) İstenilen değişikliği bir challenge olarak gör ve kendine yeni bir şeyler katacağını düşünerekten hareket et.

 

7. The only true authority stems from knowledge, not from position

Belli konular hakkında bilgi sahibi olmak beraberinde yetkili olmayı da gerektirir ve yetkide beraberinde saygılı olmayı getirir. Eğer egosuz bir ortamda saygı istiyorsan öncelikle bilgi üretmen gerekir.

 

8. Fight for what you believe, but gracefully accept defeat

Bazen sahip olduğunuz fikirlerin veya ortaya attığınız tezlerin reddedilebileceği gerçeğini unutmayın bu her zaman ihtimal dahilindedir. Bazen haklı olduğun durumlarda bile asla intikam almaya çalışmayın hatta "ben sana demiştim.." vs gibi şeyler dahi söylememeye özen gösterin.

 

9. Don’t be the “coder in the corner”

Ofisin en karanlık köşesinde oturup mesai saati boyunca sadece kişisel ihtiyaçlarını karşılamak için yerinden kalkan kişi olmayın çünkü o developer gözden uzak olduğu gibi gönülden de ırak olur. Her zaman için ofis içinde olup bitenlerden haberiniz olsun ve her zaman kendinizi ekibinizin ve şirketinizin bir parçası olarak görün.

 

10. Critique code instead of people – be kind to the coder, not the code

Mümkün olduğunca yazacağın yorum satırlarında pozitif bir dil kullanmaya çalış ve yorumlarında dünyaları yeniden yarattığından çok kod kalitesinin gelişimine yönelik açıklamalarda bulun. Yorum standartlarınız standart olup resmi bir dil kullanmaya özen gösterin ve her zaman için projeye sizden sonra gelecek olan kişiyi düşünün.

Yazının başında da bahsettiğim gibi ego makul düzeyde olduğu sürece güzeldir. Yaptığımız işlerle övünürken başkalarını ezme düşüncesine girmemeliyiz ve bırakın başkaları bizi övsün. Böyle olduğu taktirde çalışma hayatı hem siz hemde çalışma arkadaşlarınız için daha eğlenceli ve çekilesi olacaktır.