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. 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.

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 altbö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. Dolayısıyla, erişim niteleyicilerinin konulmasına gerek yoktur.
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.
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ıda 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, buraya 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 virtual) 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.
#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
#ifndef ICOMPARABLE_HXX#define ICOMPARABLE_HXXnamespace 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.
#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.
#ifndef IKESIRLISAYI_H#define IKESIRLISAYI_Hstruct _IKESIRLISAYI;typedef struct _IKESIRLISAYI* IKesirliSayi;typedef IKesirliSayi (*BOL)(IKesirliSayi ileti_alici, IKesirliSayi digerSayi); // throws SifirBolentypedef 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
#ifndef ICOMPARABLE_H#define ICOMPARABLE_Htypedef 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, vtable 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.
#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 */...