Enkapsülasyon nedir ?

Programlama dilleri açısından bakıldığında, enkapsülasyon(kapsülleme) bir modüle veya bir sınıfa ait verilere, yalnızca o sınıf veya modüldeki fonksiyonlar tarafından erişilebilmesi anlamına gelir. Enkapsüle bir yapıda bu sınıf veya modüldeki verilere public olarak direk erişim mümkün olmayacaktır. Getter ve setter fonksiyonlardan yararlanılacaktır.

Bu sayede yanlış değer ataması yapılması engellenmiş olur. Bir setter fonksiyona verilen input, gerekli yere yazılmadan önce kontrol edilip, hatalı ise yazılması engellenebilir.

Aynı zamanda enkapsüle ortamda static fonksiyonlara yine dışarıdan erişilemez; ancak aynı modül içindeki dışarıdan erişilebilen fonksiyonlar , static fonksiyonları çağırabilir. Burada yine bazı kontroller yapılarak static fonksiyonları çağırırken hatalı işlemler yapılması (hatalı input değeri verilmesi gibi) enkapsülasyon sayesinde engellenebilir. Fakat yazımda bu kısma değinmeyeceğim.

Encapsulation Nedir? | Kemal Burak Yılmaz


Enkapsülasyon nesne tabanlı programlamanın yapı taşlarından olsa da, uygun teknikler kullanılarak C programlama dilinde de farklı tasarımlarla uygulanabilir.

Bu yazımda C programlama dilinde enkapsülasyon işlemini basit bir örnekle açıklayacağım.

Enkapsülasyon işlemi yapmak istediğim ogrenci.c modülünü aşağıda paylaşıyorum. Bu modülde ogrenci tipindeki struct’tan yaratılabilmekte(create_new fonksiyonu ile), yaratılmış struct’ların alt üyeleri setter ve getter fonksiyonlarla(set_numara , get_sinif gibi) yazılıp okunabilmektedir. Fonksiyonların detaylı açıklamalarına gireceğim.

//ogrenci.c

#include "ogrenci.h"
#include <string.h>
#include <stdlib.h>

typedef struct 
{
    int numara;
    int sinif;
} ogrenci;


ogrenci_t create_new(void)
{
  ogrenci_t p_ogrenci = 0;
  p_ogrenci = malloc(sizeof(ogrenci));
  memset(p_ogrenci , 0 , sizeof(ogrenci));
  return p_ogrenci;
}
 
 
void set_numara(ogrenci_t p_ogrenci , int numara)
{
    if(p_ogrenci != 0)
    {		
       ((ogrenci *)(p_ogrenci))->numara = numara;
    }
}


int get_numara(ogrenci_t p_ogrenci)
{
    int retVal = 0;
	
    if(p_ogrenci != 0)
    {
      retVal = ((ogrenci *)(p_ogrenci))->numara;
    }	

    return retVal;
}


void set_sinif(ogrenci_t p_ogrenci , int sinif)
{
    if(p_ogrenci != 0)
    {
	if((sinif >= 9) && (sinif <= 12))
	{
	  ((ogrenci *)(p_ogrenci))->sinif = sinif;
	}
	else
	{
	  printf("Sinif degeri 9 ile 12 arasinda olabilir !!");
	}
    }
}


int get_sinif(ogrenci_t p_ogrenci)
{
    int retVal = 0;
	
    if(p_ogrenci != 0)
    {
      retVal = ((ogrenci *)(p_ogrenci))->sinif;
    }
	
    return retVal;
}

void delete_current(ogrenci_t p_ogrenci)
{
    memset(p_ogrenci , 0 , sizeof(ogrenci));
	
    ogrenci * p_ogrPtr = p_ogrenci;
	
    free(p_ogrPtr);
}

Şimdi de bu modülü include edip kullanabilmek amacıyla hazırlanmış header dosyasını inceleyelim. (ogrenci.h)

//ogrenci.h

typedef void * ogrenci_t;

 
ogrenci_t create_new(void);
void set_numara(ogrenci_t p_deneme , int num);
int get_numara(ogrenci_t p_deneme);
void set_sinif(ogrenci_t p_ogrenci , int sinif);
int get_sinif(ogrenci_t p_ogrenci);

typedef void * ogrenci_t;

Header dosyasında , ogrenci_t tipi “void pointer” olarak tanımlanmıştır. void pointer olduğundan, ogrenci.c modülündeki ogrenci tipinin alt üyelerine erişim hakkı yoktur.

ogrenci_t create_new(void) fonksiyonunda :

ogrenci_t tipinden tanımlanmış değişkene(aslen void pointer) , ogrenci.c dosyasındaki ogrenci tipindeki struct boyutundaki bellek alanının adresi atanmıştır. Ve bu değişken döndürülerek, kullanıcıya sunulmuştur.

void set_numara(ogrenci_t p_ogrenci , int numara) fonksiyonunda :

create_new fonksiyonu ile yeni üretilmiş ogrenci struct’ının pointer’ı, input olarak verilmiştir. Bu input ogrenci_t tipinde olduğundan, ogrenci struct’ının alt üyelerine erişim izni olmayacaktır. Bu sebeple fonksiyon içinde p_ogrenci pointer’ı, ogrenci struct’ının pointer’ı tipine cast edilmiştir.

((ogrenci *)(p_ogrenci)) : cast işlemi.

Daha sonra bu cast edilmiş pointer üzerinden ogrenci struct’ının numara alt üyesine, fonksiyonun input’u olan numara değeri atanmıştır.

((ogrenci *)(p_ogrenci))->numara = numara; : atama işlemi.

Enkapsülasyon açısından baktığımızda, fonksiyonu çağıran kısım, alt üyelere erişemez ancak, fonksiyon kendi modülü içerisinde alt üyelere erişme yetkisine sahiptir. Böylece çağıran taraf , alt üyelere erişim haklarından izole edilmiştir.

int get_numara(ogrenci_t p_ogrenci) fonksiyonunda :

ogrenci_t tipindeki p_ogrenci pointer’ı , ogrenci tipinin pointer’ına cast edilmiş, daha sonra numara alt üyesi okunarak , fonksiyonun dönüş değeri olarak döndürülmüştür.

void set_sinif(ogrenci_t p_ogrenci , int sinif) fonksiyonunda :

Öncelikle atanmak istenen sınıf bilgisi, fonksiyonun parametrelerindeki sinif değişkeni üzerinden kontrol edilmiştir. Eğer sinif bilgisi 9 ile 12 arasında bir sayı ise, gerekli casting işlemi yapılarak alt üye ataması yapılmıştır. Böylece enkapsülasyon çerçevesinde alt üyeye yanlış bilgi girilmesi önlenmiştir.

Şimdi ise modül fonksiyonlarının main.c üzerindeki uygulamasını göstereceğim ve enkapsülasyonu nasıl sağladığımızı anlatacağım.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include "ogrenci.h"

int main(void) {
  ogrenci_t  ogr_1 = create_new();
  ogrenci_t  ogr_2 = create_new();
	
  int numara = 0;	
  int sinif = 0;

  set_numara(ogr_1 , 5);
  set_sinif(ogr_1 , 9);
	
  set_numara(ogr_2 , 7);
  set_sinif(ogr_2 , 11);
	
  numara = get_numara(ogr_1);
  sinif = get_sinif(ogr_1);
	
	
  printf("Ogr 1 numara : %d\n" , numara);
  printf("Ogr 1 sinif : %d\n\n" , sinif);
	
  numara = get_numara(ogr_2);
  sinif = get_sinif(ogr_2);
	
  printf("Ogr 2 numara : %d\n" , numara);
  printf("Ogr 2 sinif : %d\n\n" , sinif);	
}

ogrenci_t tipi void pointer şeklinde tanımlandığından(ogrenci.h içinde) , main.c dosyasında veya ogrenci.h’ı include eden başka herhangi bir ortamda , create_new fonksiyonu ile elde edilen bir pointer üzerinden ogrenci tipinin alt üyelerine erişim sağlanamaz.

Örneğin main.c ‘de : ogr_1->numara = 5; gibi bir işlem yapılamaz.

ogrenci.h’ı include eden ortamlar , ogrenci.c modülündeki ogrenci tipini tanımadıkları için , create_new ile elde edilen pointer, ogrenci tipine cast edilemez. Bu sebepten kesinlikle pointer üzerinden alt üyelere erişim yetkisi yoktur.

Alt üyelere bilgi yazabilmek veya alt üyeleri okuyabilmek için set ve get fonksiyonları kullanılmaktadır. Bu fonksiyonlar ogrenci.c dosyasında bulunmakta ve ogrenci tipini tanıdıkları için, fonksiyonlara verilen pointer’ları ogrenci tipine cast ederek alt üyelere erişim sağlayabilirler.

set_numara(ogr_1 , 5);
numara = get_numara(ogr_1);
gibi…

main.c modülünde türetilen yeni öğrencilere bilgi ataması yapabilmek ve atanan bilgiyi okuyabilmek için set ve get fonksiyonlarını kullanmak dışında bir seçenek yoktur. Böylece enkapsüle ettiğimizi söyleyebiliriz.

main.c ‘nin çalıştığındaki çıktısını aşağıdaki gibi görebiliriz.


İyi çalışmalar dilerim.

Çağatay KAYNAK
Gömülü Yazılım Mühendisi
Teknik Eğitmen
cagataykaynak@gmail.com