Arayüzler
interface, yalnızca soyut işlevler içeren bir sınıf gibidir. Hiçbir gerçekleştirme sunmaz, yalnızca bir sınıf arayüzü bildirir.
Not: Yukarıda söylenen artık doğru değil. Bu ders yazıldıktan sonra D'de yapılan değişiklikler, interface'lerin de static ve final işlevler sunmalarına izin verir. Bu dersi değiştireceğim.
Önceki derslerde gördüğümüz class'tan türeme, gerçekleştirme türemesi idi. Yeni tanımlanan sınıf, türetildiği sınıfın bütün üyelerini ve işlevlerini ediniyordu.
Türeme dersinde gördüğümüz gibi; abstract anahtar sözcüğü de üye işlevlerin ve dolayısıyla sınıfların soyut olmalarını sağlıyordu. Hatırlarsanız, soyut sınıfların kendi nesneleri oluşturulamaz; yalnızca onlardan türeyen ve o soyut işlevleri tanımlayan türlerin nesneleri oluşturulabilir.
class'tan türeme özetle şunları getirir:
- üst sınıfın gerçekleştirmesini (üyelerini ve üye işlevlerini) edinme
- soyut işlevleri tanımlama zorunluluğu
interface, bütünüyle soyut işlevlerden oluşan bir sınıf olarak düşünülebilir. Tek yaptığı, belirli bir arayüz belirlemek, ve bu arayüzün alt sınıflar tarafından tanımlamalarını şart koşmaktır. Kendi üyeleri veya üye işlevleri bulunamaz, ve bir class'tan türeyemez. Dolayısıyla üye edinmeye değil, belirli bir tür gibi kullanılabilmeyi garanti etmeye yarar.
interface'ten türeme özetle şunu sağlar:
- bildirdiği bütün işlevlerin, alt sınıflardan en az birisi tarafından tanımlanma zorunluluğu
Tanımlanması
class yerine interface yazılarak tanımlanır:
interface SesliAlet { // ... }
interface, o arayüzün gerektirdiği işlevleri bildirir; ama tanımlarını vermez:
interface SesliAlet { string ses(); // Yalnızca bildirilir (tanımı verilmez) }
O arayüz ile kullanılabilmeleri için, interface'ten türeyen sınıfların interface'in bildirdiği işlevleri tanımlamaları gerekir.
interface'ten türetme
Türeme söz dizimi class'tan farklı değildir:
class Keman : SesliAlet { string ses() { return "♩♪♪"; } } class Çan : SesliAlet { string ses() { return "çın"; } }
Üst sınıflarda da olduğu gibi; parametre olarak interface alan işlevler, onları asıl türlerini bilmeden kullanabilirler. Örneğin işlemleri sırasında bir SesliAlet kullanan bir işlev, hangi tür bir sesli alet olduğunu bilmeden onun ses işlevinden yararlanabilir:
void sesliAletKullan(SesliAlet alet) { // ... bazı işlemler ... writeln(alet.ses()); // ... başka işlemler ... }
Sınıflarda da olduğu gibi, o işlev SesliAlet arayüzünden türeyen her sınıf ile çağrılabilir:
sesliAletKullan(new Keman); sesliAletKullan(new Çan);
Her aletin kendi asıl türünün tanımladığı ses işlevi çağrılır ve sonuçta sırasıyla Keman.ses ve Çan.ses üye işlevlerinin çıktısı görülür:
♩♪♪ çın
Birden fazla interface'ten türetme
Bir sınıf, ancak tek bir class'tan türetilebiliyordu. interface'ten türemede ise böyle bir kısıtlama yoktur.
Örneğin haberleşme aletlerini temsil eden şöyle bir arayüz olduğunu düşünelim:
interface HaberleşmeAleti { void konuş(string mesaj); string dinle(); }
Telefon diye bir sınıfı hem sesli bir alet, hem de bir haberleşme aleti olarak kullanabilmek için onu bu iki arayüzden birden türeterek tanımlayabiliriz:
class Telefon : SesliAlet, HaberleşmeAleti { // ... }
O tanım şu iki ilişkiyi birden sağlar: "telefon bir sesli alettir", ve "telefon bir haberleşme aletidir".
Nesnelerinin oluşturulabilmesi için, Telefon sınıfının bu iki arayüzün gerektirdiği bütün işlevleri tanımlaması gerekir:
class Telefon : SesliAlet, HaberleşmeAleti { string ses() // SesliAlet için { return "zırrr zırrr"; } void konuş(string mesaj) // HaberleşmeAleti için { // ... mesajı hatta ilet ... } string dinle() // HaberleşmeAleti için { string hattaDuyulanSes; // ... sesi hattan oku ... return hattaDuyulanSes; } }
Bu örnekte görüldüğü gibi iki interface ile sınırlı değildir; programın gerekleri doğrultusunda sınırsız sayıda interface'ten türetilebilir.
interface'ten ve class'tan türetme
Bir sınıf; bir veya daha fazla interface'ten türetilmenin yanında, bir adet olduğu sürece aynı zamanda bir sınıftan da türetilebilir:
class Saat { // ... kendi gerçekleştirmesi ... } class ÇalarSaat : Saat, SesliAlet { string ses() { return "bi bi biip"; } }
ÇalarSaat, Saat'in bütün üyelerini ve üye işlevlerini edinmenin yanında, bir de SesliAlet arayüzünün gerektirdiği ses işlevini tanımlamak zorundadır.
interface'ten interface türetme
Başka bir arayüzden türetilen bir arayüz, kendisinden türetilecek olan sınıfların kendi bildirdiği işlevleri de tanımlamalarını gerektirir:
interface MüzikAleti : SesliAlet { void akortEt(); }
Yukarıdaki tanıma göre; bir MüzikAleti olabilmek için, hem SesliAlet'in gerektirdiği ses işlevini, hem de kendi gerektirdiği akortEt işlevini tanımlamak gerekir.
Örneğin yukarıdaki Keman sınıfını doğrudan SesliAlet arayüzünden türetmek yerine MüzikAleti'nden türetsek, onun bildirdiği akortEt işlevini de tanımlamamız gerekir:
class Keman : MüzikAleti { string ses() // dolaylı olarak SesliAlet için { return "♩♪♪"; } void akortEt() // MüzikAleti için { // ... akort işlemleri ... } }
Ne zaman kullanmalı
Oldukça sık kullanılır. Hemen hemen bütün sıradüzenlerin en üstünde bir veya daha fazla interface bulunur. En sık karşılaşılan sıradüzenlerden birisi, tek bir interface'ten türeyen basit gerçekleştirme sınıflarından oluşan sıradüzendir:
MüzikAleti
(interface)
/ | \ \
Kemençe Saz Kaval ...
Daha karmaşık sıradüzenlerle de karşılaşılır, ama bu basit yapı çoğu programın ihtiyacı için yeterlidir.
Bütün alt sınıflarda da bulunan ortak gerçekleştirmelerin bir ara sınıfta tanımlandığı durumlarla da sık karşılaşılır. Alt sınıflar bu ortak sınıftan türerler. Aşağıdaki sıradüzende TelliMüzikAleti ve NefesliMüzikAleti sınıfları, kendi alt türlerinin ortak üyelerini ve üye işlevlerini içeriyor olabilir:
MüzikAleti
(interface)
/ \
TelliMüzikAleti NefesliMüzikAleti
/ | \ / | \
Kemençe Saz ... Kaval Ney ...
O ortak sınıflardan türeyen alt sınıflar da kendi daha özel tanımlarını içerebilirler.
Soyutlama
Arayüzler programların alt bölümlerini birbirlerinden bağımsızlaştırmaya yararlar. Buna soyutlama denir. Örneğin müzik aletleri kullanan bir programın büyük bir bölümü yalnızca MüzikAleti arayüzünden haberi olacak şekilde, ve yalnızca onu kullanarak yazılabilir.
Müzisyen gibi bir sınıf, asıl türünü bilmeden bir MüzikAleti içerebilir:
class Müzisyen { MüzikAleti alet; // ... }
Birden fazla müzik aletini bir araya getiren türler, o aletlerin asıl türlerini bilmek zorunda değillerdir:
MüzikAleti[] orkestradakiAletler;
Programın çoğu işlevi yalnızca bu arayüzü kullanarak yazılabilir:
bool akortGerekiyor_mu(MüzikAleti alet) { bool karar; // ... return karar; } void güzelÇal(MüzikAleti alet) { if (akortGerekiyor_mu(alet)){ alet.akortEt(); } writeln(alet.ses()); }
Bu şekilde bir soyutlama kullanarak programın bölümlerinin birbirlerinden bağımsız hale getirilmeleri, alt sınıflarda ileride gerekebilecek kod düzenlemelerinin serbestçe yapılabilmelerini sağlar. Alt sınıfların gerçekleştirmeleri bu arayüzün arkasında oldukları için, bu arayüzü kullanan kodlar o değişikliklerden etkilenmemiş olurlar.
Örnek
Yukarıdaki bütün arayüzleri ve sınıfları içeren bir program şöyle yazılabilir:
import std.stdio; interface SesliAlet { string ses(); } class Çan : SesliAlet { string ses() { return "çın"; } } interface MüzikAleti : SesliAlet { void akortEt(); } class Keman : MüzikAleti { string ses() { return "♩♪♪"; } void akortEt() { // ... akort işlemleri ... } } interface HaberleşmeAleti { void konuş(string mesaj); string dinle(); } class Telefon : SesliAlet, HaberleşmeAleti { string ses() { return "zırrr zırrr"; } void konuş(string mesaj) { // ... mesajı hatta ilet ... } string dinle() { string hattaDuyulanSes; // ... sesi hattan oku ... return hattaDuyulanSes; } } class Saat { // ... Saat'in gerçekleştirilmesi } class ÇalarSaat : Saat, SesliAlet { string ses() { return "bi bi biip"; } // ... ÇalarSaat'in gerçekleştirilmesi } void main() { SesliAlet[] aletler; aletler ~= new Çan; aletler ~= new Keman; aletler ~= new Telefon; aletler ~= new ÇalarSaat; foreach (alet; aletler) { writeln(alet.ses()); } }
main'in içindeki aletler bir SesliAlet dizisi olduğu için, o diziye SesliAlet'ten türeyen her tür eklenebiliyor. Sonuçta programın çıktısı bütün aletlerin ürettikleri sesleri içerir:
çın ♩♪♪ zırrr zırrr bi bi biip
Özet
interfacebir arayüz tanımlar; bütün işlevleri soyut olan bir sınıf gibidir- Bir sınıfın "o türden" nesnelerinin olabilmesi için o arayüzün bildirdiği bütün işlevleri tanımlaması gerekir
- Tek
class'tan türetebilme kısıtlamasıinterface'lerde yoktur; sınıflar ve arayüzler birden fazlainterface'ten türetilebilirler - En sık karşılaşılan sıradüzenlerden birisi, üstte bir arayüz (
interface), ve alttaki gerçekleştirmeleridir (class)
D.ershane
Forum
Wiki
Projeler
Tanıtım
İletişim
Hakları