C#Yazılım

C# Serisi – 0x04 – Interface Yapısı ve Kullanımı

Merhaba,

C# serisinin 4.bölümünde Nesne Yönelimli Programlamada önemli bir yeri olan Interface konusunu inceleyeceğiz. Bu bölüm içerisinde interfacelerin yazılımda nerede ve nasıl kullanıldığından bahsederken örnek simülasyon kodlarlada anlattıklarımı destekleyeceğim. Bu aşamada sadece odaklanmamız gereken konu interfaceler olduğu için bu blogta interface ile sık sık karıştırılan veya bir projeye dahil edilme noktasında hangisinin seçileceğinin karıştırılması gibi konulara girmeyeceğiz. Daha sonra bu konuları detaylıca inceleyeceğimiz için sonraki yazılara gözatabilirsiniz. Bu blog içerisinde standartlaşmış tanımlama kalıplarının yanısıra burada yeni duyacağınız ifadeleri gerçek hayattan örneklerle anlatmaya çalışacağım. Paylaşacağım kodlarda aynı şekilde gerçek hayatın simülasyonu niteliğinde olacaktır. Interfacelerin anlatımında 2 farklı C# simülasyon projesini sizlerle paylaşacağım. Bunlardan bir tanesi bir bankacılık sisteminin sadece interfacelerle ilgili olan kısmını paylaşacağım ve açıklamalarını yapacağım. Yazının sonunda ise ikinci alternatif bir proje olarak bir oyun satıl alma ve oyuncu üyelik sisteminin simülasyonu niteliğindeki kodları sizin incelemeniz için en alt kısımda paylaşacağım.

İyi okumalar dilerim. Umarım tam manasıyla istifade edebilirsiniz. Eksik gördüğünüz yerleri katkı sağlamak ve birbirimizi geliştirmek adına benimle paylaşırsanız minnettar olurum.

 

Interface Nedir? Ne işe Yarar? (Senaryo)

Interfaceler, nesne yönelimli programlama soyutlama yapmak amacıyla kullanılırlar. Bu noktada öncelikle “Soyutlama” kavramına bir açıklık getirmemiz gerekiyor. Soyutlama kavramını bir tanımlama kalıbıyla anlatmak yerine gerçek hayattan bir örnek ile anlatmak istiyorum. Örneğin bir bankacılık sisteminde yer alan farklı kredi türlerinin hesaplanmasına yönelik bir proje geliştirdiğimizi varsayalım. Örneğin X bankası müşterilerine Konut Kredisi, Taşıt Kredisi ve İhtiyaç Kredisi hizmetleri verebiliyor olsun. Bu durumda bir müşteri X Bankasına geldiği zaman banka görevlisi kişinin hangi kredi türünden faydalanmak istediğini soracaktır. Kredi almak isteyen kişi ise birden fazla (taşıt kredisi ve ihtiyaç kredisi arasından kararsız bir müşteri olabilir.) kredi hesaplaması yaptırarak her bir kredi türü için bilgi almak isteyebilir. Bu durumda banka görevlisi bizim tasarladığımız sistemin arayüzünden örneğin taşıt kredisini ve ihtiyaç kredisini seçerek bunları hesaplatmak isteyecektir.

Şimdi bu durumda sistemin yapması gereken işlem, hem taşıt kredisini hemde ihtiyaç kredisini verilen parametreler ile hesaplaması ve bir değeri return etmesi gerekmetedir. Burada yazılımcı olarak her bir kredi türü için farklı sınıflar (class) oluşturmamız gerekmektedir. Şimdi ana programda (program.cs) seçilen kredi türleri için tek tek snıfılardan referans alarak bunlar ile işlem yapmak hem karmaşıklığa yol açacaktır hemde sonradan kodun geliştirilmesine yönelik problemler doğuracaktır. Bu noktada ana programdan sadeec tek bir yerden referans alınarak birbirinin alternatifi olan durumlara bir interface yardımıyla yönlendirme yapmamız gerekir. Burada anlatılanları kodları incelediğiniz zaman daha iyi anlayacaksınzıdır. Yani burada 3 farklı kredi hesaplama sınıfımız var bu sınıflarda ortak alanlar var. Biz bu ortak alanları bir interface yardımıyla tanımlayacağız ve daha sonra hesaplanması istenilen kredi türü hangisiyse o kredi türünün sınıfı içerisindeki Hesapla() fonksiyonu çalıştırılacaktır.

Biraz daha içeriye girecek olursak şöyle anlatabiliriz. Her bir kredi hesaplama sınıfının içerisinde mutlaka kendisine özel bir Hesapla() operasyonu yer alacaktır. Bu hesapla() operasyonu interface içerisinde sadece imza olarak tanımlanacak ve her bir kredi hesaplama sınıfı bu operasyonu implemente edecektir. Ana programdan (Banka görevlisinin seçim ekranından) seçilen kredi türleri interface’imizden kalıtım alan ilgili classın içerisindeki Hesapla() operasyonu ile hesaplanacaktır.

 

Soyutlama kavramını tek bir cümle ile özetlenecek olursa, iç detayların gizlenerek sadece işlevlerin gösterilmesine soyutlama denir.

 

Peki interface kullanmadan her bir sınıf için ana programdan referans alarak hesaplatamaz mıyız ?

Interface konusunda akıllara ilk gelen soru mutlak olarak böyle bir sorudur. Sorunun cevabı ise “Evet tabikide bu şekildede yapabilirdik ama…”.

Pekala bu şekilde de yapabilirdik fakat bunu yaptığımız zaman her şeyden önce ana program içerisinde gereksiz bir karmaşa ve kod kalabalığı olacaktır. Daha önemlisi ise şudur;

Buradaki örneğimizde olduğu gibi X Bankasının şimdilik tanımladığımız 3 farklı kredi türü hizmeti bulunmaktadır. Fakat 1 yıl sonra X Bankası “Kardeşim ben 5 Farklı kredi türünde daha hizmet vermeye başlayacağım. Sistemi bu doğrultuda revize edelim.” derse ?

Böyle bir durumda her bir krediyi tek tek ana programdan hesaplatmış olsaydık her bir kredi türü ekleme işleminde gelip tüm adımları yeniden yazmamız ve ana program içerisinde (zaten karmaşık bir kod olacak !) ilgili kısımları tek tek bularak güncellememiz gerekecekti. Burada eklenecek olan kredi sayısı 5 yerine 100 olsa ne olacak peki? İşte bu noktada SPAGETTİ dediğimiz mevzuya doğru ilerliyor olacaktık ! (Bkz. Spagetti Kod)

Interface sayesinde bu karmaşıklığa hiç girmeden örnek kodumuzda olduğu gibi IKrediManager interface’inden kalıtım alarak yeni kredi hesapla sınıfı oluşturup kolayca yeni kredi hesaplamalarını sisteme entegre etmiş olacağız. Zaten bu şekilde yapmadığımız zaman SOLID yazılım geliştirme prensiblerinin O harfiyle temsil edilen Open-Closed Principle – prensibine  aykırı bir kodlama  gerçekleştirmiş olacağız demektir. Bu prensib sisteme yeni özellikler eklendiğinde mevcut özelliklerin bu yenilikten etkilenmemesini ifade etmektedir. Detaylı bilgi için mutlaka araştırılmalıdır. (Bknz. SOLID Yazılım Prensibleri)

 

Kodları incelemeden önce interfacelerin özellikleri nelerdir, kullanımı nasıldır ve nerede nasıl kullanılabilir kısaca bunları açıklayalım.

 

Interfacelerin Özellikleri

  • Interfaceler isimlendirilirken başına “I” getirilir. Böylece diğer sınıflarla bir karışıklık olmaması ve interafacelerin kolayca anlaşılması sağlanır.
  • Interface’ler sınıfların referans numaralarını tutarlar.
  • Interface’ler birbirinin alternatifi olan ama kod içerikleri farklı olan durumlarda kullanılırlar. (Farklı kredi türlerinin hesaplanması)
  • Interface’ler default olarak public kabul edilirler ve public olmak zorundadırlar.
  • Interfaceler başka bir interface tarafından inherit edilebilirler.
  • Classlar birden fazla interfaceden çoklu kalıtım alabilirler. (Multiple-İnheritance) (SOLID – Interface Segregation Principle)
  • New anahtar sözcüğü ile nesneleri oluşturulamaz.
  • İnterfaceler bir sınıfın sadece ne yapması gerektiğini belirtiri nasıl yapacağını söylemez.
  • Interface’ten kalıtım alan tüm sınıfların interface içerisinde bulunan tüm metodları implemente etmesi zorunludur.
  • Interfaceler içerisinde metotların sadece imzaları yer alır. İçi dolu metot interface içerisinde bulunmaz.
  • Classlar somut birer nesneyken, interfaceler Soyut Nesnelerdir.
  • Interfaceler instance değişken içermezler.Herhangi bir şekilde değiştirilemezler.

 

Bir Interface Nasıl Tanımlanır

Visual Studio ile çalışanlar için Solution Explorer içerisinde ilgili projenize sağ tıkladıktan sonra add  altında class seçeneğine tıkladıktan sonra açılan sayfada interface seçeneğini seçip isimlendirme yaptıktan sonra imzalarınızı ekleyebilirsiniz.

 

Örnek olarak bir interface içerisinde metot imzalarının nasıl tanımlandığını aşağıdaki kodu inceleyerek anlayabilirsiniz. Yazının başlangıcında giriş yaptığımız senaryoya uygun olarak IKrediManager adında  bir interface tanımlayalım ve Hesapla() metodunun imzasını oluşturalım.

using System;
using System.Collections.Generic;
using System.Text;

namespace OOP3
{
    interface IKrediManager
    {
        public void Hesapla();
        void BiseyYap();                      
    }
}

Bu kod parçasında IKrediManager isimli bir interface oluşturduk daha sonra içerisine Hesapla() ve BiseyYap() adlı iki tane metot imzası tanımladık. Görüldüğü gibi burada metotların içerisini boş bıraktık. Metotlar için herhangi bir süslü parantez dahi açmadık. Bir interface tanımı gördükten sonra tek bir program dosyası içersisinde bir interface oluşturarak diğer sınıflardan nasıl inherit edieleceğinide görelim.

Bu kod parçasında IPerson() interface’i, Customer Class’ı ve Student Class’ını oluşturalım.

 

interface IPerson
{
  ....
}

class Customer:IPerson
{
  ....
}

class Student:IPerson
{
  ....
}

Bu kod parçasıyla kısaca şunu demek istedik. Öğrenci bir kişidir. Aynı zamanda müşteride bir kişidir. Öğrenci ve müşteri kişilerinin ortak özelliklerini IPerson interface’i içerisinde tanımlayacağız. Örneğin FirstName, LastName, TcNo gibi özellikler hem müşterilere ait hemde öğrencilere ait birer özelliktir. Burada Customer ve Student sınıfları IPerson interface’i içerisinde yer alan tüm metotları implement etmek zorundadır. Aksi halde program hata verecektir. Eğer sonradan IPerson interface’ine bir imza eklenirse bu metotlar IPerson interface’inden kalıtım alan tüm sınıflara implement edilmek zorundadır.

Interface’lerin özellikleri arasında new anahtar sözcüğü ile nesneleri oluşturulamaz demiştik. Yani bir interface’in instance’ı oluşturulamaz. Çünkü bunlar soyut nesnelerdir. Fakat interfacelerden şu şekilde instance oluşturabiliriz.

IPerson person1 = new Customer();

IPerson person2 = new Student();

Görüldüğü üzere interface üzerinden bir tane müşteri ve bir tanede öğrenci nesnesi oluşturduk. Bu şekilde interfaceleri biraz daha anlaşılabilir kıldığımıza inanıyorum. Daha sonra işleyeceğimiz abstract class’larda interfaceler gibi new anahtar sözcüğü ile nesne oluşturulmasına izin vermez.

 

Senaryonun Simülasyon Kodlarını Yazalım…

Tekrar senaryomuza dönecek olursak şu şekilde IhtiyacKrediManager sınıfımızı oluşturabiliriz.

IhtiyacKrediManager:

using System;
using System.Collections.Generic;
using System.Text;

namespace OOP3
{
    class IhtiyacKrediManager : IKrediManager
    {
        public void BiseyYap()
        {
            throw new NotImplementedException();
        }

        public void Hesapla()
        {
            Console.WriteLine("İhtiyaçs kredisi ödeme planı hesaplandı.");
        }
    }
}

 

Aynı şekilde KonutKrediManager ve TasitKrediManager sınıflarımızıda oluşturarak IKrediManager interface’inden kalıtım alalım.

KonutKrediManager:

using System;
using System.Collections.Generic;
using System.Text;

namespace OOP3
{
    class KonutKrediManager : IKrediManager  //Ebeveyn "KrediManager"
    {
        public void BiseyYap()
        {
            throw new NotImplementedException();
        }

        public void Hesapla()
        {
            Console.WriteLine("Konut kredisi ödeme planı hesaplandı.");
        }
    }
}

 

TasitKrediManager:

using System;
using System.Collections.Generic;
using System.Text;

namespace OOP3
{
    class TasitKrediManager : IKrediManager
    {
        public void BiseyYap()
        {
            throw new NotImplementedException();
        }

        public void Hesapla()
        {
            Console.WriteLine("Taşıt kredisi ödeme planı hesaplandı.");
        }
    }
}

 

Bu aşamaya kadar geldikten sonra kredi hesaplama metotlarımız istenilen kredi türüne göre hesaplanabilecektir. Ben burada tek tek hesaplama kısmını yapmadım tabikide. Burada sadece senaryomuzdaki sistemin simülasyonunu gerçekleştiriyoruz. Bu noktada önemli olan interface ve classlar arası bağlantılı yapıyı doğru bir şekilde oluşturmaktır.

Şimdi gerekli olan interface ve class’larımızı oluşturduğumuza göre artık ana program (program.cs) içerisinden gerekli seçimleri yaparak kredi hesaplamalarını yaptırabiliriz. Ana programımız senaryo açısından şöyle düşünülmelidir. Banka görevlisi tarafından kullanılan ekran ve bu ekranda option olarak tanımlanan kredi türleri seçilerek programa girdi olarak kullanıcı tarafından verilmektedir. Biz sadece simülasyon gerçekleştirdiğimiz için burada değerleri program.cs içerisinde tanımlayacağız.

 

BasvuruManager.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace OOP3
{
    class BasvuruManager
    {                   
        public void KrediOnBilgilendirmesiYap(List<IKrediManager> krediler)
        {
            foreach (var kredi in krediler) //Listedei her bir kredinin hesaplanması
            {
                kredi.Hesapla();

            }
        }
    }
}

 

Program.cs:

using System;
using System.Collections.Generic;

namespace OOP3
{
    class Program
    {
        static void Main(string[] args)
        {
            
            IKrediManager ihtiyacKrediManager = new IhtiyacKrediManager();       
            IKrediManager tasitKrediManager = new TasitKrediManager();            
            IKrediManager konutKrediManager = new KonutKrediManager();                       

            //Ekrandan birden fazla kredi aynı anda seçilirse.
            List<IKrediManager> krediler = new List<IKrediManager>() {ihtiyacKrediManager, konutKrediManager};

            basvuruManager.KrediOnBilgilendirmesiYap(krediler);                

        }
    }
}

 

Tüm kodlarımızı tamamladıktan sonra artık projeyi çalıştırabiliriz. Run butonuna tıkladığımızda aşağıdaki çıktıyı almamız gerekiyor. Bu çıktıdaki loglama ifadesini blog içerisinde anlatmadım. Çünkü IKrediManager yapısının benzeri olarak farklı loglama seçeneklerinin sunulmasınıda aynı proje içerisinde sağlamış olduk. Github linkini aşağıda sizlerle paylaşacağım. Mutlaka öncesinde kendiniz bu kısmı yazmaya çalışın daha sonra linkten kodları inceleyerek hatalarınızı kontrol edin. Ancak bu şekilde kendinizi geliştirebilirsiniz.

 

 

Kendin Yap : IKrediManager interface’ini iyice anladıktan sonra 2 farklı loglama seçeneğini sisteme entegre ediniz ve ILoggerService interface’ini oluşturunuz. (Dosyaya Loglama ve Sql Loglama gibi.)

 

Konunun özeti interfaceler yapılacak, verilecek olan hizmetleri ve özellikleri tanımlamakla görevlidir. Tanımlanan işlevlerin nasıl yapılacağı ise alt sınıfların görevidir. Böylece sisteme kullanıcıları ve mevcut kodları hiç etkilemeden yeni özellikler ve hizmetler eklenebilir. Buda programınıza esneklik kazandıracaktır.

 

Umarım sizin için faydalı olmuştur. Bende sizler gibi öğrendiğim bilgileri hem pekiştirmek hemde bilgi paylaştıkça çoğalır felsefesine olan inancımdan dolayı sizlere aktarmaya çalışıyorum. Okuduğunuz için teşekkür ederim.

Mutlu ve sağlıklı günler dilerim…

 

Projeleri yıldızlamayı ve takip etmeyi unutmayın!


Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir