Arayüz (yazılım)
Nesne yönelimli programlama dillerinde arayüz, değişik sınıflardan nesnelerin kategorize edilmesini sağlayan bir soyut tür çeşitidir. Tanımlanmakta olan kategorinin birbirleriyle alakasız sınıfları ortak bir çatı altında toplaması nedeniyle, arayüz tanımları, soyut sınıfların aksine, listeledikleri iletilerin gerçekleştirim ayrıntısı olan herhangi bir bilgi içeremezler. Dolayısıyla, bir arayüz tanımı iletilere karşılık gelen bir altyordam gövdesi veya altalan tanımı içeremez. Bir başka açıdan bakarsak, arayüz tanımında yer alan programlama öğelerinin zaman içinde değişme olasılığı düşük öğeler olması gerekir. Buna göre, arayüz tanımları gerçekleştirimci ile kullanıcının paylaştığı ve sabit olma özelliği bulunan altyordam imzaları ile simgesel sabit tanımlarını barındırabilir.
Kullanılan programlama dili tarafından doğrudan desteklenmediği durumlarda arayüz kavramı, altyordam gövdeleri içermeyen ve altalan barındırmayan bir soyut sınıf tanımı ile benzetilebilir. Soyut sınıf kavramının doğrudan desteklenmediği yordamsal dillerde ise, arayüzde bulunması beklenen programlama öğelerinin başlık dosyası benzeri bir dosyada toplanması sağlanarak arayüz kavramına öykünülebilinir. Ancak, derleyici desteğinin bulunmaması nedeniyle, her iki kullanımın da hataya açık olduğu unutulmamalıdır.
Nesneleri Kategorize Etmekte Arayüz
Sınıf kavramı ve kalıtlama ilişkisinin sınıfları sıradüzenine koyarak bir terminoloji oluşturmasına benzer bir şekilde, arayüz kavramı ve gerçekleştirme ilişkisi de sınıfları kategorilere ayırır. Herhangi bir arayüzü gerçekleştiren sınıflar, arayüzün tanımladığı kategoriye ait sayılır ve bu sınıfların nesneleri gerçekleştirilen arayüzün tutacağı ile kullanıldıklarında, aynı kategoride bulundukları için, çokbiçimli bir şekilde kullanılabilirler.
Ne kastedildiği sıklıkla kullanılan bir işlemle daha iyi anlaşılacaktır: sıralama. Bir grup verinin belirli bir öncelik-sonralık ilişkisine uygun olarak yeniden düzenlenmesi şeklinde tanımlanabilecek sıralama işlemi, söz konusu verilerin karşılaştırılabilir olmasını gerektirir. Zira, hangi verinin önce, hangi verinin sonra geleceği ancak karşılaştırma ile anlaşılabilir.[1] Sıralanmak istenebilecek verilerin çeşitliliği düşünüldüğünde, karşılaştırma işleminin üstsınıfta yapılacak dinamik iletimli bir atyordam tanımının altsınıflarda ezilmesi ile gerçekleştirilemeyeceği görülür. Çünkü, böylesine bir çözüm üstsınıf ve altsınıflar arasında bir benzerlik ilişkisini gerektirir. Halbuki, birbiriyle alakasız pek çok kavram karşılaştırma işlemini desteklemek durumunda kalabilir. Karşılaştırma işleminin sınıf sıradüzeninin kök sınıfına taşınması da bir çare olmayacaktır; böylesine bir uygulama, matemetikteki matrisler gibi karşılaştırılamaz değerlerin de karşılaştırılabilir olduğu algısına neden olacaktır. Gerçek çözüm, karşılaştırılabilirlik kategorisine karşılık gelen bir arayüzün tanımlanması ve isteyen sınıfların bu arayüzü gerçekleştirmesinden geçer.[2]
Yazılım Bileşenleri Geliştirmede Arayüz
Yazılım geliştirmede arayüz kavramının temel kullanım amacı standart oluşturmaktır. Birbirini tamamlayan bileşenlerin bağımsız bir biçimde geliştirilmesine olanak tanımak ve aynı işlevi gören bileşenlerin tak-çıkar mantığıyla birbirinin yerine kullanılmasını kolaylaştırmak için böylesine bir yaklaşım zorunludur. Bu sayede, pazardan alınacak hazır bileşenler ve bileşenlerin paralel geliştirilmesinin yardımıyla daha güvenilir ürünlerin daha hızlı geliştirilmesi mümkün olacaktır. Bu, aşağıdaki zaman sıralamasına göre verilmiş etkinliklerden daha iyi anlaşılacaktır.
- Bir şirketler konsorsiyumu, B1 bileşenine ilişkin arayüzleri tanımlar.
- Ş1 şirketi, tanımlı standarda uygun bir B1 bileşeni geliştirir.
- Ş2 şirketi, B1, B2 ve B3 bileşenlerinin kullanıldığı Ü1 ürününü geliştirir.
- B1 bileşeni Ş1 şirketinden alınır.
- Arayüzleri tanımlanan B2 ve B3 bileşenleri bağımsız iki proje grubu tarafından Ş2 şirketi bünyesinde geliştirilir.
- B1, B2 ve B3 bileşenleri birleştirilerek Ü1 oluşturulur.
- Ş2 şirketi Ü1 ürününü piyasaya sürer.
- Ş3 şirketi, tanımlı standarda uygun bir B1 bileşeni geliştirir.
- Ş2 şirketi, bir sonraki sürümün performansını geliştirmek adına, B2 ve B3 bileşenlerini elden geçirir.
- Müşterilerden Ü1'e ilişkin performans şikayetleri gelir.
- Ş1 şirketi, B1 bileşenini Ş3'ten aldığı gerçekleştirim ile değiştirir. Ayrıca, B2 ve B3'ün yeni uyarlamaları Ü1'e tümleştirilir.
- Müşteriler daha yüksek performanslı yeni ürünü, kendi yazılımlarında hiçbir değişiklik yapmadan kullanır.
Dikkat edilecek olursa, Ü1'in piyasaya sürülmesinden sonra Ş3 tarafından geliştirilen B1 bileşeni sorun olmaksızın kullanılabilmektedir. Aynı şekilde, Ü1'in performansı geliştirilmiş uyarlaması da müşterinin daha önceden geliştirdiği yazılımın içinden rahatlıkla kullanılmaktadır. Geleceğe yapılan bu yolculukların her iksi de, arayüz tanımları sayesinde mümkün olmaktadır. Bunu, tadilat geçiren bir evin bölüm bölüm değiştirilmesine benzetebiliriz: doğramaların değiştirilmesi için evin yıkılıp yeniden yapılması gerekmez; aynı standarda uygun üretilen doğramanın eskisinin yerine takılması yeterli olacaktır.
Arayüz Gerçekleştirme Yoluyla Çokbiçimlilik
Sınıfların ortak paydası olarak tanımlanması, arayüzlerdeki iletilerin çokbiçimli olarak kullanılabilmesini olanaklı kılar; arayüz tutacağı yoluyla gönderilecek iletiler, tutacağın temsil ettiği nesnenin sınıfındaki ilişkin gerçekleştirimin kullanılmasına neden olacaktır. Yandaki şekilden izleyecek olursak, nesneye gönderilecek iletiler öncelikle arayüz tutacağının eleğinden geçer. Arayüz türünde listelenmeyen iletiler derleyici tarafından reddedilecektir. Derleyici denetiminden geçen programın çalıştırılması sırasında ise, denetim akışının ileti gönderiminin yapıldığı yere gelmesi arayüz tutacağının gösterdiği nesnenin sınıfındaki—gerçekleştirilmiş veya kalıtlanmış—aynı imzaya sahip altyordamın çağrılmasına neden olur. Arayüz tutacağının gösterdiği nesnenin türünün, tutacak türünü doğrudan veya dolaylı bir biçimde gerçekleştiren bir sınıf olması zorunludur.
Arayüz gerçekleştirme ve arayüz tutacağının bu şekilde kullanılması, sınıf kalıtlama ve üstsınıf tutacağının kullanılması ile gelen çokbiçimliliğin getirdiği avantajları getirir. Arayüzdeki iletileri kullanarak yazılmış kod, arayüzü gerçekleştiren tüm sınıflar için çalışacak ve böylece aynı iskelete sahip kod parçalarının değişik sınıflar için tekrar tekrar yazılmasının önüne geçilmiş olacaktır. Bu ise, kodda yapılması istenen bir değişikliğin tek bir yerde yapılması anlamına gelir ki, kodun yeniden kullanımını sağlayan böylesine bir yaklaşım kod bakımını da kolaylaştıracaktır.
Programlama Dilleri ve Arayüz Kavramı
Kimi programlama dilleri arayüz kavramını dildeki bir yapı ile doğrudan desteklerken diğerleri var olan yapıların disiplinli bir biçimde kullanılması yoluyla dolaylı olarak destekler. Takip eden alt bölümlerde her iki gruba dair örnekler verilmektedir. Dikkat edilecek olursa, ne şekilde desteklenirse desteklensin, arayüz tanımında herhangi bir gerçekleştirim ayrıntısının ele verilmediği, kullanıcı ile paylaşılan programlama öğelerinin—altyordam imzaları ve simgesel sabitler—değişmesi düşük olasılıklı öğeler olduğu görülecektir. Bu kısıtlamanın temelinde arayüzün kullanıcı kodu ile gerçekleştirimci arasında bir sözleşme olması özelliği ve gerçekleştirimcinin yapacağı değişikliklerin kullanıcı kodunda değişikliklere yol açma ihtimalini en aza indirmek isteği yatar.
Java
Arayüz kavramını interface
yapısı ile doğrudan destekleyen Java'da programcının yapacağı olası bir hata kavramdan haberdar derleyici sayesinde önlenir. Programlama dili standardındaki tanımlara uygun gerçekleştirilmiş derleyiciler, metot gövdesi ve altalan tanımı gibi gerçekleştirim ayrıntısı olarak addedilen programlama öğelerinin arayüze konulmasına izin vermez. Ayrıca, standart görevi görme ve kullanıcı kodu ile gerçekleştirimci arasında sözleşme tanımlama özelliklerinin bir sonucu olarak, arayüz tanımında yer alan tüm öğelerin public
erişime sahip olduğu kabul edilir.[3] Dolayısıyla, erişim niteleyicilerinin konulmasına gerek yoktur.
- IKesirliSayı.java[4]
package matematik;
import matematik.ayrıksıdurumlar.SıfırBölen;
public interface IKesirliSayı {
public IKesirliSayı böl(IKesirliSayı) throws SıfırBölen;
IKesirliSayı çarp(IKesirliSayı);
public IKesirliSayı çıkar(IKesirliSayı);
public IKesirliSayı topla(IKesirliSayı);
public double değer();
public long pay();
public long payda();
} // IKesirliSayı arayüzünün sonu
Arayüz tanımının derlenip sınıf yolu üzerinde uygun bir yere konulması sonrasında yapılması gereken, söz konusu kategoriye giren kavramların karşılığı olan sınıflarda bu arayüzün, belki de diğer arayüzlerle birlikte, gerçekleştirilmesidir. Bu noktada, arayüz(ler) ve sınıf arasındaki gerçekleştirme ilişkisinin bir taahhüt olduğu ve arayüz(ler)de listelenen tüm iletilerin gerçekleştirilmesi gerektiği unutulmamalıdır.[5]
- KesirliSayı.java
package matematik;
import matematik.IKesirliSayı;
import matematik.ayrıksıdurumlar.SıfırBölen;
public class KesirliSayı implements IKesirliSayı, Comparable<IKesirliSayı> {
...
public IKesirliSayı böl(IKesirliSayı) throws SıfırBölen { ... }
public IKesirliSayı çarp(IKesirliSayı) { ... }
public IKesirliSayı çıkar(IKesirliSayı) { ... }
public IKesirliSayı topla(IKesirliSayı) { ... }
public double değer() { ... }
public long pay() { ... }
public long payda() { ... }
public int compareTo(IKesirliSayı diğerSayı) { ... }
...
private IKesirliSayı sadeleştir() { ... }
...
} // KesirliSayı sınıfının sonu
IKesirliSayı
ve Comparable
arayüzlerinin gerçekleştirimine örnek olarak yukarıda sunulan iskeletin içi değişik şekillerde doldurulabilir. Ancak, ayrıntıda farklılık gösteren tüm sınıfların ortak bir özelliği olacaktır: gerçekleştirdikleri IKesirliSayı
ve Comparable
arayüzleri. Bu ise, söz konusu sınıfların nesnelerinin, yukarıda28 Eylül 2015 tarihinde Wayback Machine sitesinde arşivlendi. da anlatıldığı gibi, IKesirliSayı
veya Comparable
arayüzü türündeki tutacaklar vasıtasıyla çokbiçimli bir şekilde kullanılabileceği anlamına gelir. (Java'da arayüzler hakkında daha ayrıntılı bilgi için, buraya19 Ocak 2012 tarihinde Wayback Machine sitesinde arşivlendi. bakınız)
C++
Arayüz kavramını doğrudan desteklemeyen C++ programlama dilinde programcıların belirli kurallara uyulan sınıf tanımlarıyla bu eksiği gidermeleri mümkündür. Öncelikle, altyordam gövdelerinin sağlanmaması koşulu, tüm fonksiyon tanımlarının dinamik iletimli boş fonksiyonlar (İng., pure virtual2 Ekim 2011 tarihinde Wayback Machine sitesinde arşivlendi.) olarak yapılması gerektiği anlamını taşır. Bu koşula uyulmaması, yapılmakta olan tanımı soyut/somut sınıf tanımına dönüştürür. Bir diğer önemli nokta ise, arayüzdeki tüm öğelerin kamuya açık ilan edilmesinin sonucudur: sınıf tanımının hemen başına public:
konulması bir alışkanlık haline getirilmeli ve gerçekleştirim ayrıntısı olmanın habercisi diğer erişim niteleyicilerinden kaçınılmalıdır. Ayrıca, kullanıcı ile paylaşılacak olan arayüz tanımının C++ kaynak dosyası yerine başlık dosyasına konulması da iyi bir alışkanlık olacaktır.
- IKesirliSayi.hxx
#ifndef IKESIRLISAYI_HXX
#define IKESIRLISAYI_HXX
#include "matematik/ayriksidurumlar/SifirBolen.hxx"
using namespace matematik::ayriksidurumlar;
namespace matematik {
class IKesirliSayi {
public:
IKesirliSayi bol(IKesirliSayi) throw(SifirBolen) = 0;
IKesirliSayi carp(IKesirliSayi) = 0;
IKesirliSayi cikar(IKesirliSayi) = 0;
IKesirliSayi topla(IKesirliSayi) = 0;
long bolen(void) = 0;
long bolunen(void) = 0;
double deger(void) = 0;
}; // IIKesirliSayi arayuzunun sonu
} // matematik aduzayinin sonu
#endif
- IComparable.hxx
#ifndef ICOMPARABLE_HXX
#define ICOMPARABLE_HXX
namespace sistem {
template <class V>
class IComparable {
public:
int compareTo(V) = 0;
}; // IComparable arayuzunun sonu
} // sistem aduzayinin sonu
#endif
Tanımların tamamlanması ve ilişkin başlık dosyalarının kütük dizgesinde uygun yerlere konulması sonrasında sıra arayüzlerin gerçekleştirimine gelir. Arayüz kavramını desteklemeyen C++, doğal olarak, gerçekleştirme ilişkisinden de haberdar değildir. Dolayısıyla, gerçekleştirme ilişkisi yerine kalıtlama ilişkisini koymamız gerekecektir.
- KesirliSayi.cxx
#include "sistem/IComparable.hxx"
using namespace sistem;
#include "matematik/IKesirliSayi.hxx"
#include "matematik/ayriksidurumlar/SifirBolen.hxx"
using namespace matematik::ayriksidurumlar;
namespace matematik {
class KesirliSayi : public IKesirliSayi, public IComparable<IKesirliSayi> {
...
public:
IKesirliSayi bol(IKesirliSayi) throw(SifirBolen) { ... }
IKesirliSayi carp(IKesirliSayi) { ... }
IKesirliSayi cikar(IKesirliSayi) { ... }
IKesirliSayi topla(IKesirliSayi) { ... }
long bolen(void) { ... }
long bolunen(void) { ... }
double deger(void) { ... }
int compareTo(IKesirliSayi digerSayi) { ... }
...
}; // KesirliSayi sinifinin sonu
} // matematik aduzayinin sonu
Arayüz ve bu arayüzü gerçekleştiren sınıfın kullanımına gelindiğinde bir noktanın unutulmaması yararlı olacaktır. Dinamik iletimli fonksiyonların çokbiçimli kullanılabilmesi için nesnelerin referans veya gösterici aracılığıyla kullanılması gerekir. Aksi takdirde, tüm çağrılar çalışma öncesinde bağlanacak ve kodun çokbiçimli kullanılması söz konusu olmayacaktır.
C
Yordamsal bir dil olan C, nesne paradigmasının gerektirdiği en temel kavramları dahi desteklemez. Dolayısıyla, sınıf, arayüz gibi dil düzeyi kavramların işletim dizgesi kavramı olan dosyadan yararlanarak gerçekleştirilmesi zorunludur. Genel strateji, C++'da uygulanan ile aynıdır: başlık dosyasında tanımlanan arayüzün C dosyasında gerçekleştirilmesi. Arayüz tanımında, gerçekleştirim ayrıntısı ele vermemek adına, dinamik iletimli çağrılacak fonksiyonların adreslerini tutan yapının dışında tanım verilmemeli, statik iletimli çağrılacak fonksiyonların extern
ile nitelenerek verilen deklarasyonu ile yetinilmelidir. Ayrıca, fonksiyonların işleyeceği ortak bir bellek bölgesi söz konusu ise, bunun tanımının da deklarasyon ile geçiştirilmesi ve asıl tanımın C dosyasına bırakılması gereklidir. Son olarak, nesne paradigmasında alıcı nesneyi temsilen tüm iletilere geçirilen saklı argüman (this
veya self
), derleyicinin nesne kavramından haberdar olmadığı düşünülerek, tüm fonksiyonlara ayrıca geçirilmelidir.
- IKesirliSayi.h
#ifndef IKESIRLISAYI_H
#define IKESIRLISAYI_H
struct _IKESIRLISAYI;
typedef struct _IKESIRLISAYI* IKesirliSayi;
typedef IKesirliSayi (*BOL)(IKesirliSayi ileti_alici, IKesirliSayi digerSayi); // throws SifirBolen
typedef IKesirliSayi (*CARP)(IKesirliSayi ileti_alici, IKesirliSayi digerSayi);
typedef IKesirliSayi (*CIKAR)(IKesirliSayi ileti_alici, IKesirliSayi digerSayi);
typedef IKesirliSayi (*TOPLA)(IKesirliSayi ileti_alici, IKesirliSayi digerSayi);
typedef long (*PAY)(IKesirliSayi ileti_alici);
typedef long (*PAYDA)(IKesirliSayi ileti_alici);
typedef double (*DEGER)(IKesirliSayi ileti_alici);
typedef struct _ARAYUZ_KESIRLISAYI {
BOL bol;
CARP carp;
CIKAR cikar;
TOPLA topla;
DEGER deger;
PAY pay;
PAYDA payda;
} *KesirliSayi_Arayuzu;
#endif
- IComparable.h
#ifndef ICOMPARABLE_H
#define ICOMPARABLE_H
typedef void* Object;
typedef int (*COMPARETO)(Object, Object);
typedef struct _ARAYUZ_ICOMPARABLE {
COMPARETO compareTo;
} *Comparable_Arayuzu;
#endif
Gerçekleştirim kısmına geldiğimizde, nesne yönelimli dillerde derleyiciler tarafından yapılan bazı şeyleri programcının yapması gerektiği unutulmamalıdır. Örneğin, nesnenin özelliklerine göre sağlanan fonksiyon gerçekleştirimleri ile fonksiyon göstericileri ilklenmeli—yani, vtable22 Temmuz 2010 tarihinde Wayback Machine sitesinde arşivlendi. programcı tarafından oluşturulmalı—yığın bellekten yer ayrımı işi yapıcı içinde yapılmalı ve nesnemiz ne kadar basit bir yapıya sahip olursa olsun yıkıcı görevini görecek bir fonksiyon yazılmalıdır.
- KesirliSayi.c
#include <stdlib.h>
#include "matematik/IKesirliSayi.h"
#include "sistem/IComparable.h"
struct _IKESIRLISAYI {
KesirliSayi_Arayuzu _ksa;
Comparable_Arayuzu _ca;
long _pay, _payda;
};
IKesirliSayi KesirliSayi_bol(IKesirliSayi ileti_alici, IKesirliSayi diger_sayi) { ... }
/* Diğer fonksiyon gerçekleştirimleri */
KesirliSayi_Arayuzu KesirliSayi_ksa_gosterici(void) {
static KesirliSayi_Arayuzu gosterici = NULL;
if (!gosterici) {
gosterici = (KesirliSayi_Arayuzu) malloc(sizeof(struct _ARAYUZ_KESIRLISAYI));
gosterici->bol = KesirliSayi_bol;
/* Diğer fonksiyon göstericilerinin ilklenmesi */
} /* if (!gosterici) */
return gosterici;
} /* KesirliSayi_Arayuzu KesirliSayi_ksa_gosterici(void) sonu */
Comparable_Arayuzu KesirliSayi_ca_gosterici(void) {
static Comparable_Arayuzu gosterici = NULL;
if (!gosterici) {
gosterici = (Comparable_Arayuzu) malloc(sizeof(struct _ARAYUZ_COMPARABLE));
gosterici->compareTo = KesirliSayi_karsilastir;
} * if (!gosterici) */
return gosterici;
} /* KesirliSayi_Arayuzu KesirliSayi_ksa_gosterici(void) sonu */
IKesirliSayi KesirliSayi_yarat(long pay, long payda) {
IKesirliSayi nesne = (IKesirliSayi) malloc(sizeof(struct _IKESIRLISAYI));
nesne->_ksa = KesirliSayi_ksa_gosterici();
nesne->_ca = KesirliSayi_ca_gosterici();
nesne->_pay = pay;
nesne->_payda = payda;
return nesne;
} /* yapici(long, long) sonu */
void KesirliSayi_yik(IKesirliSayi* nesne) {
if (*nesne == NULL) return;
free(*nesne);
*nesne = NULL;
} /* yikici sonu */
...
Notlar
- ^ Aslında, bu ifade tamamen doğru değil. Sıralanmak istenen değerleri ikil içeriklerine göre gruplayarak işini gören bazı algoritmalar kastettiğimiz anlamda karşılaştırma yapmazlar. Ancak, bu algoritmalarda dahi, ikillerin içeriğine göre gruplama yapılırken 1'in 0'dan büyük olduğu varsayılarak gizli de olsa karşılaştırma yapılmaktadır.
- ^ Bunun örneğini Java platformunda sağlanan
Comparable
arayüzünde görüyoruz.String
,java.math.BigInteger
gibi sınıflar bu arayüzü gerçekleştirirkenThread
sınıfı gerçekleştirmemektedir. - ^ Standartların kamuya açık olma özelliğini düşündüğünüzde bunun çok doğal bir varsayım olduğunu görürsünüz.
- ^ Türkçe içerikli Java kaynak kodu ile sıkıntı yaşıyorsanız, bu yazı 28 Temmuz 2011 tarihinde Wayback Machine sitesinde arşivlendi. size yardımcı olacaktır.
- ^ Java platformunda tanımlanmış bazı özel arayüzler için bu kuralın gevşetildiği ifade edilmeli. Bir örnek vermek gerekirse; iki iletiye sahip
java.util.Comparator
arayüzündekiequals
'ın gerçekleştirilmemesi derleyici tarafından anlayışla karşılanacak ve bu iletinin gerçekleştirimininObject
sınıfındaki ile aynı olduğu varsayılacaktır.