Interface Nedir, Nerelerde ve Neden Kullanırız ?

// 30 Mayıs 2010 // Delphi

Şu son bir kaç gündür, Delphi 2010 ve getirdiği yenilikler ile ilgileniyorum. Bu bağlamda; Generic Liste sınıfları ve bu liste sınıflarının özelleştirilmesi ve çeşitli yeteneklerle zenginleştirilmesi üzerine geliştirmeler yapıyorum. Sizlerle bu yaptıklarımı paylaşabilmem için öncelikle bazı temel hususları izah etmek gerektiğini düşündüm. Bu bağlamda; ilk olarak Interface’ler hakkında bir makale, ardından operator overloading konusu ve nihayet generic’ler ve kendi kendini yoketme becerisine sahip bazı özel mekanizmaları işleyeceğiz. Aslında tüm bu hususlara girmeden, özetle nelerin yapılabileceğini paylaşıp örneğimi verebilir ve geçebilirdim. Ancak bu yaklaşım tarzı; benim programlamaya bakış açıma aykırı olduğu ve sizlere pek bir şey kazandıracağına inanmadığım için kendi adıma zor olanı seçip, mümkün mertebe en derinlere kadar inmeye gayret edeceğim.

Hedefimize ilerlerken ilk durağımız olan Interface’lerde biraz durup soluklanalım ve bu kavramın ne olduğu yada ne olmadığı; neleri başarıp neleri başaramayacağı; neden var olduğu ve nerelerde kullanabileceğimiz hakkında nacizane bilgilerimiz ile konunun karanlık köşelerine ışık tutmaya gayret edelim.

Interface kelimesi dilimizde genellikle “Arayüz” yada “Arabirim” olarak kullanılmaktadır. Pek çok programcımız için Interface’ler ya çok basit bir yapı olarak ya da COM programlamanın fıtratı gereği çok karmaşık bir yapı olarak algılanır. Aslında her ikisi de doğrudur. Yapısal görünümü itibari ile son derece basit görünen Interface’ler aslında yapabildikleri ve nasıl yaptıkları itibarı ile de son derece kompleks yapılardır. Bir interface görünüm ve yapı itibari ile bir sınıfa çok benzer. Ancak elbette bu makaleye konu olan pek çok farklılıkları da mevcuttur. Arzu ederseniz makalemizde ilerlemeden önce çok basit bir interface tanımının Delphi’deki yapısını görelim ve ardından ilerlemeye devam edelim:

  IFlyable = interface
  end;

Gördüğünüz gibi bir sınıf tanımına son derece benziyor. Ancak bazı farklılıkları var. IFlyable(uçabilen) arabiriminin tanımlamasında “class” ayrılmış sözcüğü yerine “interface” sözcüğünün geçtiğini gözlemliyoruz. Şu anda gözlemleyebildiğimiz başka bir fark bulunmuyor. Aslında interface’ler özet sınıf adını verdiğimiz abstract sınıflara çok benzerler. Abstract sınıflar, kendisini miras alacak sınıflar için ileride kullanılmasının zorunlu olduğu metodların birer özetlerini içerirler. Ancak bu sınıflar aynı zamanda gerçek kod bloklarına da ev sahipliği yapabilirler. Interface’lerin ve abstract(özet) sınıfların ortak özellikleri; kendilerini kullanacak olan alt sınıflarda tanımlanması gereken metodların şablonlarına ev sahipliği yapmaktır.

Bir interface’te yada abstract sınıfta tanımlanmış olan bir metod; bu interface yada abstract sınıfı kullanmak isteyen alt sınıflarda kesinlikle tanımlanmalıdır(Gerçekte bir abstract sınıf içinde tanımlı olan bir metod, bu abstract sınıfı miras alan alt sınıfta tanımlanmayabilir, ancak alt sınıftaki metod çağrımı bir hataya neden olacağı için genellikle tanımlanmak zorundadır.). Aksi durumda derleyici kodunuzu derlemenize izin vermez. Şu aşamaya kadar söylediklerimizi küçük bir örnek ile göstermeye çalışalım;

  IFlyable = interface
    procedure Fly;
  end;

  TPlaneInterface = class(TInterfacedObject, IFlyable)
    procedure Fly;
  end;

  TFlyable = class abstract
    procedure Fly; virtual; abstract;
  end;

  TPlaneClass = class(TFlyable)
    procedure Fly; override;
  end;

Yukarıdaki kısa kod örneğimizde bir interface ve o interface’i implemente(uygulayan) eden bir sınıf; bir abstract sınıf ve o sınıfın bir mirasçısını görüyorsunuz. Tanımlarda bazı farklılıklar var. IFlyable interface’i içindeki Fly metodu için editörümüzde herhangi bir kod yazmanıza gerek yoktur. Ancak bu interface’i implemente edecek olan sınıfta (TPlaneInterface) Fly metodunun yazılması zorunludur. Bu tanıma çok benzer bir şekilde TFlyable isimli abstract sınıfımızında kod editöründe herhangi bir kodu yoktur ve interface’lerde olduğu gibi abstract sınıfı miras alan (TPlaneClass) sınıflarda Fly metodunun yeniden tanımlanması icap eder. Eğer TPlaneClass sınıfı içinde Fly metodunu yeniden tanımlamazsanız derleyici kodunuzu düzgün bir şekilde derleyebilecektir ancak çalışma anında Fly metodunu çağırırsanız bir “Abstract Error” hatası alırsınız. Bu hataların oluşmasına mani olmak için, abstract sınıflarda genellikle “virtual; abstract” olarak tanımlanan metodların alt sınıflarda yeniden tanımlanması gerekir.

Bu bağlamda interface’ler ve abstract sınıflar birbirlerine son derece benzerler. Genellikle programcılarımız abstract sınıfların varlığı karşısında interface’lerin karmaşık ve gereksiz mekanizmalar olduğunu düşünürler. Abstract sınıflar interfacelerden farklı olarak içlerinde gerçek kod parçacıkları da bulundurabilirler. Bu yetenekleri ile interface’lerden daha geniş bir kullanıma sahip oldukları ve aynı zamanda daha nesnesel ve kolay oldukları düşünülür. Bu elbette bir bakış açısıdır, ancak tamamiyle doğru değildir.

Şimdi bir abstract sınıfın içinde gerçek kod parçalarının da olabileceğine bir örnek verelim;

  TFlyable = class abstract
   private
    fField : Integer;

    procedure Fly; virtual; abstract;
   public
    procedure RealCode; virtual;
  end;
 //...
 //...
  procedure TFlyable.RealCode;
  begin
    //..
    //..
  end;

Görüldüğü üzere, bir abstract sınıfın içinde hem şablon bir metod hemde gerçek bir kod yazabildik. Aynı zamanda public, private gibi erişim belirleyicileri kullanabildik ve ihtiyacımız olan değişkenleri de tanımlayabildik.

Evet bu noktaya kadar bir interface’in yada bir abstract sınıfın neye benzediği yönünde küçük bazı örnekler gördük. Ancak interface’in neden var olduğu, abstract ve interface’lerin birbirlerine alternatifmi oldukları hakkında henüz herhangi bir yorum yapmadık.

Interface’ler; içlerinde istediğiniz kadar metod tanımlayabileceğiniz ve bu metodlar için kod yazmanıza gerek olmayan sanal yapılardır. Bu interface’i implemente edecek olan sınıflarınızın yeteneklerinin ne olduğunu özetleyebilen özet yapılar olarak düşünülebilir. Interface’ler içlerinde metod tanımlarını barındırabilirler ancak, değişken tanımına yada erişim kısıtlayıcılara sahip olamazlar. Yani; bir interface’in içinde herhangi bir türden değişken tanımlayamazsınız , tanımlayabildiğiniz tüm metodlar; public olarak kabul görür ve interface’in içinde public, private, protected kullanamazsınız. Bu özellikleri ile abstract sınıflardan ayrılırlar.

Aynı zamanda Interface’ler bir sınıftan yada bir record’dan türeyemezler. Bir Interface; sadece bir başka interface’den türeyebilir. Benzer bir şekilde herhangi bir sınıfta direkt olarak bir interface’den türeyemez.

Geçersiz kullanımlara kısaca bir göz atalım arzu ederseniz;

  ITest = interface
  end;

  IFlyable = interface(TObject) // Geçersiz
  end;

  IFlyable = interface(ITest) // Geçerli
  end;

  TFlyable = class abstract(IFlyable) // Geçersiz
  end;

  TFlyable = class abstract// Geçerli
  end;

  TFlyable = class abstract(TComponent) // Geçerli
  end;

  TFlyable = class abstract(TInterfacedObject, IFlyable) // Geçerli
  end;

Görüldüğü gibi, bir interface bir sınıftan miras alınarak tanımlanamaz. Ancak ve ancak bir başka interface’i miras alabilir. Oysaki sınıf tanımlarında abstract olsun yada olmasın durum biraz daha farklıdır. Bir sınıf bir interface’i direkt olarak miras alamaz ancak bir yardımcı sınıf vasıtası ile(TInterfacedObject) interface’leri de miras alabilir.

Tüm bu anlatılanlar ışığında kısa bir özet vermek sanırım iyi olacaktır.

  • Interface’ler şablon tanımlarıdır, ve gerçek kod parçacıkları içermezler.
  • Interface’ler içlerinde değişken tanımlarına ve erişim kısıtlayıcılarına(public, private vb.) sahip olamazlar.
  • Interface’ler sınıflardan miras alınamazlar, sadece bir başka interface’den miras alınabilirler.
  • Interface’leri implemente eden sınıflarda interface’in sahip olduğu tüm metodların uygulanması gerekir.
  • Abstract sınıflar şablon tanımlarıdır ama gerçek kod parçacıkları da içerebilirler.
  • Abstract sınıflar içlerinde değişken tanımlarına ve erişim kısıtlayıcılarına(public, private vb.) sahip olabilirler.
  • Abstract sınıflar interfacelerden direkt olarak miras alınamazlar, Interface’leri implemente edebilmeleri için TInterfacedObject gibi bir sınıfı kullanmaları gerekir.
  • Abstract sınıfları implemente eden sınıflarda abstract sınıfın içinde tanımlı olan tüm virtual; abstract; metodların implemente edilmesi önerilir ancak bunun aksi bir durum derleyici tarafından da kabul görür. Bu gibi bir durumda derleyici abstract sınıfı miras alan sınıfı create etmeye çalıştığınız kod bloğu için bir warning mesajı verecektir.

Sanıyorum artık, abstract sınıflar ve interface’lerle ilgili az da olsa bir fikir sahibiyiz. Bu iki birbirine yakın kullanım temelde aynı amaca hizmet etmektedir. Ancak birbirinden farklı bir sözdizimine sahiptir. Peki, biz hangi durumlarda hangisini kullanacağız ?

Bu sorunun yanıtı abstract sınıf ve interface ayrımına gidilmesinin nedenlerini izah için yeterli olacaktır. Hepimizin bildiği gibi Delphi, C# gibi diller multiple inheritance adı verilen çoklu kalıtımı desteklemezler. Ancak C++ gibi diller çoklu kalıtıma imkan sağlarlar. Bir sınıfın birden fazla atasının olması gibi bir konseptin Delphi, C# gibi dillerde olmaması belki karmaşanın önüne geçmek içindir. Ancak çoklu kalıtıma ihtiyaç duyduğumuz yerlerde olabilir. Kısa bir örnek vererek daha da anlamlandırmaya çalışalım.

Diyelim ki, taşıtlarla ilgili bir sınıf hiyerarşisi oluşturmak istiyoruz. Bu hiyerarşiyi hem interface ile hem de abstract sınıflar ile yapmaya çalışalım:

// Interfacelerle ilgili tanımlamalar...

  ITasit = interface
    procedure MotoruCalistir;
    procedure MotoruDurdur;
  end;

  IUcabilenTasitlar = interface(ITasit)
    procedure Uc;
  end;

  TUcabilenTasitlar = class(TInterfacedObject, IUcabilenTasitlar)
  public
    procedure MotoruCalistir;
    procedure MotoruDurdur;

    procedure Uc;
  end;

  IYuzebilenTasitlar = interface(ITasit)
    procedure Yuz;
  end;

  TYuzebilenTasitlar = class(TInterfacedObject, IYuzebilenTasitlar)
  public
    procedure MotoruCalistir;
    procedure MotoruDurdur;

    procedure Yuz;
  end;

  IKaradaGidebilenTasitlar = interface(ITasit)
    procedure Yuru;
  end;

  TKaradaGidebilenTasitlar = class(TInterfacedObject, IKaradaGidebilenTasitlar)
  public
    procedure MotoruCalistir;
    procedure MotoruDurdur;

    procedure Yuru;
  end;

// Abstract sınıflarla ilgili tanımlamalar...

  TTasitAbstract = class abstract
  public
    procedure MotoruCalistir; virtual; abstract;
    procedure MotoruDurdur; virtual; abstract;
  end;

  TUcabilenTasitlarAbstract = class abstract(TTasitAbstract)
  public
    procedure Uc; virtual; abstract;
  end;

  TUcabilenTasitlar = class(TUcabilenTasitlarAbstract)
  public
    procedure MotoruCalistir; override;
    procedure MotoruDurdur; override;

    procedure Uc; override;
  end;

  TYuzebilenTasitlarAbstract = class abstract(TTasitAbstract)
  public
    procedure Yuz; virtual; abstract;
  end;

  TYuzebilenTasitlar = class(TYuzebilenTasitlarAbstract)
  public
    procedure MotoruCalistir; override;
    procedure MotoruDurdur; override;

    procedure Yuz; override;
  end;

  TKaradaGidebilenTasitlarAbstract = class abstract(TTasitAbstract)
  public
    procedure Yuru; virtual; abstract;
  end;

  TKaradaGidebilenTasitlar = class(TKaradaGidebilenTasitlarAbstract)
  public
    procedure MotoruCalistir; override;
    procedure MotoruDurdur; override;

    procedure Yuru; override;
  end;

// Interface sınıflarından yada abstract sınıflardan türeyebilen sınıflar

  TUcak = class(TUcabilenTasitlar)
  end;

  TGemi = class(TYuzebilenTasitlar)
  end;

  TAraba = class(TKaradaGidebilenTasitlar)
  end;
..
..
..
var
  BMW, Audi, Mercedes  : TAraba;
  Gemi, Yat, Tekne  : TGemi;
  Piper, Jet, Zeplin  : TUcak;
begin
  BMW := TAraba.Create;
  BMW.Yuru;

  Audi := TAraba.Create;
  Audi.Yuru;

  Mercedes := TAraba.Create;
  Mercedes.Yuru;

  Gemi := TGemi.Create;
  Gemi.Yuz;

  Yat := TGemi.Create;
  Yat.Yuz;

  Tekne := TGemi.Create;
  Tekne.Yuz;

  Piper := TUcak.Create;
  Piper.Uc;

  Jet := TUcak.Create;
  Jet.Uc;

  Zeplin := TUcak.Create;
  Zeplin.Uc;

  BMW.Free;
  Audi.Free;
  Mercedes.Free;

  Gemi.Free;
  Yat.Free;
  Tekne.Free;

  Piper.Free;
  Jet.Free;
  Zeplin.Free;
end;

Yukarıdaki sınıf tanımlarımızda uçabilen, yüzebilen ve karada gidebilen taşıtları hem interface’ler vasıtası ile hem de abstract sınıflar vasıtası ile tanımladık. Ardından TAraba, TGemi ve TUcak sınıflarını bu sınıflardan türettik ve ilgili nesnelerimizi oluşturabildik. Bu aşamaya kadar herhangi bir dizayn problemi ile karşılaşmadık. Ancak; ya hem karada hem denizde gidebilen howercraft türü bir taşıtımız varsa ne yapacağız ? Bu aracı hangi sınıftan türeteceğiz ? Abstract yaklaşımla yapmaya çalıştığımız da aşağıdaki gibi bir kullanıma ihtiyacımız olacak;

  THemKaradaHemDenizdeGidebilenTasitlarAbstract = class abstract(TYuzebilenTasitlarAbstract, TKaradaGidebilenTasitlarAbstract)
  end;

Ancak hepimizin bildiği gibi ne Delphi ne de C# gibi diller bu tarz bir tanımlamaya(Multiple Inheritance) müsaade etmiyorlar. Arzu ederseniz bir de Interface’ler ile yapmaya çalışalım;

  THemKaradaHemDenizdeGidebilenTasitlar = class(TInterfacedObject, IYuzebilenTasitlar, IKaradaGidebilenTasitlar)
   public
    procedure MotoruCalistir; // ITasit interface'inden..
    procedure MotoruDurdur; // ITasit interface'inden..

    procedure Yuz; // IYuzebilenTasitlar interface'inden..
    procedure Yuru; // IKaradaGidebilenTasitlar interface'inden..
 end;
..
..
..
var
  howercraft : THemKaradaHemDenizdeGidebilenTasitlar;
begin
  howercraft := THemKaradaHemDenizdeGidebilenTasitlar.Create;
  howercraft.Yuz;
  howercraft.Yuru;
  howercraft.Free;
end;

Evet, görebildiğiniz gibi Delphi’de interface kullanarak multiple inheritance’ın yollarını açtık. Multiple Inheritance’a ihtiyaç duyulması interface kullanılması için gereken nedenlerden sadece birisidir. Diğer önemli neden, şu ana kadar hiç bahsetmediğimiz Referans Sayma Mekanizmasıdır. Makalemizin bu noktadan sonrası konunun başlığı gereği interface’lere yönelik olacaktır. Şu zamana kadar yazdıklarımız, genelde birbirine karıştırılan yada birbirinin arasındaki farkın çok net olmadığı iki özel teknolojinin zihnimizde daha da netleşebilmesi adına idi.

Referans sayımının detaylarına inmeden önce tüm interface’lerin IInterface arabiriminden türemiş olduğunu söylememiz gerekir. Nasıl ki yeni bir sınıf tanımlarken TMyClass = class tanımı ile TMyClass = class(TObject) tanımı aynı ise; interfacelerde de benzer bir şekilde IMyInterface = interface tanımı ile IMyInterface = interface(IInterface) tanımı aynıdır. Kısacası tüm interface’lerin atası IInterface arabirimidir. Şimdi gelin IInterface arabiriminin Delphi’deki tanımına yakından göz atalım:

  IInterface = interface
    ['{00000000-0000-0000-C000-000000000046}']
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;

IInterface arabiriminin sahip olduğu 3 metodun da birbirinden önemli olduğunu ifade etmek isterim. Hatırlayacağınız üzere, interface tanımlarının bir sınıf tarafından direkt olarak miras alınmasının mümkün olmadığından bahsetmiştik. Bu sebeple bir interface’i implemente edebilmek adına örneklerimizde hep TInterfacedObject isimli sınıfı kullanmıştık. Peki nedir bu TInterfacedObject ? Bir de ona yakından bakalım;

  TInterfacedObject = class(TObject, IInterface)
  protected
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    class function NewInstance: TObject; override;
    property RefCount: Integer read FRefCount;
  end;

Gördüğünüz gibi TInterfacedObject sınıfı interface’lerin ata sınıfı olan IInterface arabirimini implemente etmiş durumda. Biz bundan sonraki örneklerimizde herhangi bir interface’i implemente ederken TInterfacedObject sınıfını kullanmaya devam edeceğiz. Ancak siz başka bir sınıftan miras almak istiyorsanız, IInterface’in içinde tanımlı olan 3 metodu da implemente etmeniz gerektiğini lütfen unutmayın.

Peki ne işe yarıyor bu 3 ayrı metod ? Bir kaç paragraf önce interface’lerin en önemli özelliklerinden bir tanesinin Referans Sayma Mekanizması olduğundan bahsetmiştik. İşte bu mekanizma bizim için son derece kullanışlı ve faydalı olacak. Delphi’de bütün interface’ler referans sayımlıdır. Kısaca referans sayımı; hafızadaki bir bellek bölgesine işaret eden herhangi bir nesneye kaç işaretçinin referansı olduğunu tutar. Hemen kısa bir örnek vererek izah etmeye gayret edelim:

type
  ITest = interface
  end;

  TTest = class(TInterfacedObject, ITest)
  public
    destructor Destroy; override;
  end;

var
  Test : ITest;

implementation

destructor TTest.Destroy;
begin
  ShowMessage('Nesne yok edilecek.!');
  inherited Destroy;
end;
..
..
var
   I1, I2, I3 : ITest;
   iRefCount : Integer;
begin
  Test := TTest.Create;
  iRefCount := TTest(Test).RefCount; // Referans = 1

  I1 := Test;
  iRefCount := TTest(Test).RefCount; // // Referans = 2

  I2 := Test;
  iRefCount := TTest(Test).RefCount; // Referans = 3

  I3 := Test;
  iRefCount := TTest(Test).RefCount; // Referans = 4
  
  I1 := nil;
  iRefCount := TTest(Test).RefCount; // Referans = 3

  I2 := nil;
  iRefCount := TTest(Test).RefCount; // Referans = 2

  I3 := nil;
  iRefCount := TTest(Test).RefCount; // Referans = 1
end;

Örneğimiz de ITest isimli herhangi bir metoda sahip olmayan bir interface ve o interface’i implemente etmiş bir basit sınıfımız var. Test değişkenimizin ve I1, I2, I3 değişkenlerimizin interface türünde olduğuna dikkat ediniz. Test := TTest.Create satırının işletilmesi hemen IInterface arabiriminin içinde tanımlı olan ve TInterfacedObject’de yeniden tanımlanan _AddRef isimli metoda dallanılmasını sağlar. Aynı şekilde I1 := Test; ataması, _AddRef metodunun yeniden çağırılmasına, dolayısı ile Test değişkeninin referans değerini 1 arttırmasına neden olur. TInterfacedObject sınıfının _Addref, _Release ve QueryInterface metodlarını nasıl uyguladığına biraz daha yakinen bakalım:

function TInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE;
end;

function TInterfacedObject._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);
end;

function TInterfacedObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;

Gördüğünüz gibi TInterfacedObject sınıfının _AddRef metodunda FRefCount isimli değişken Thread güvenlikli olarak(InterlockedIncrement) 1 arttırılıyor. _Release metodunda ise Thread güvenlikli olarak(InterlockedDecrement) 1 azaltılıyor. FRefCount değerinin 0′a düşmesi ilgili nesneye herhangi bir referans kalmaması anlamına geldiği için otomatikman Destroy metodu çağrılıyor.

Yukarıdaki örneğimizde Test : ITest tanımı global alanda yapıldığı için Destroy metodu çağrılmayacaktır. Ancak bu tanım, lokal olarak yapılsa idi ilgili procedure’den çıkıldığında otomatikman referans 1 azaltılacağından Destroy metodu çağrılabilecek ve nesnemiz yok edilecekti.! Bir interface değişkenine nil atamasının yapılması, yada o değişkenin kullanıldığı kod bloğundan çıkılması(scope’un dışına çıkılması) ilgili interface referansını 1 azaltan _Release çağrısına neden olur. Bu sayede gerçek sınıflarınıza interface değişkenlerle ulaşmanız, bellek yönetimini otomatik yapmanız anlamını taşır. Yine yukarıdaki örneğimiz için konuşacak olursak, lokal olarak tanımlanmış I1, I2 ve I3 değişkenlerine nil atamasının yapılmadığı düşünülürse, procedure’den çıkıldığında her bir değişken için otomatikman nil ataması yapılacak ve referanslar yine düzgün bir şekilde azaltılabilecektir.

Tüm bu yazdıklarımızdan sonra Interface’lerin referans sayma mekanizmasının memory leak’ler hususunda bizlere yardımcı olabildiğini görebiliyoruz. Aynı zamanda Interface’ler dilde native olarak bulunmayan multiple inheritance hususunda da bizlere yardımcı olabilmekteydi. Yukarıda tanımını verdiğimiz IInterface arabiriminin QueryInterface adlı metoduna şu ana dek hiç değinmedik. QueryInterface metodu bir nesnenin belirtilen bir interface’i implemente edip etmediğini sorgulamak için kullanılır. Örneğimizde kullandığımız gibi bazen interface referanslarımızdan nesne referanslarına dönüşüm yapmak isteriz. Bunun için ya casting uygularız yada as operatörünü kullanırız. Her iki durumda da TInterfacedObject sınıfının QueryInterface metoduna dallanılır.

Görüldüğü gibi aslında interface’ler ve abstract sınıflar birbirlerine benzer özelliklere sahip. Abstract sınıfların kalıtımdan yararlanmak, erişim belirleyicilerini kullanabilmek, değişken tanımlamalarına sahip olmak gibi özellikleri olmasına rağmen; Interface’ler de tüm bu özelliklere haiz olmamalarına rağmen multiple inheritance’a imkan sağlamak ve referans sayımı metodunun uygulanması sebebi ile otomatik yok etme mekanizmasının güzelliklerini bizlere sunabilmesi adına önemli bir kullanım alanına sahiptir.

Herhangi bir interface’i implemente etmiş olan bir sınıfa bir interface değişkeni vasıtası ile ulaşıyorsanız, o sınıfın yok edilmesi ile ilgilenmenize gerek kalmaz. Delphi sizler için bunu mükemmel bir şekilde yapabilir. Interface’ler ile ilgili konuşulacak daha başka hususlar da olabilir, ancak bu hususları anlatmak yerine sizlerin araştırmalarına ve testlerine bırakmak öğrenme yolunda faydalı bir adım olabilir.

Interface’lerle ilgili kısıtlı miktardaki bilgimi sizlerle paylaştıktan sonra, bir sonraki adımımız Operator Overloading ve ardından da Delphi ortamında istisnasız her sınıfı otomatikman yok edebilme becerisine sahip olacak bir tasarımı sizinle paylaşmak olacaktır.

Saygılar, sevgiler..

“Interface Nedir, Nerelerde ve Neden Kullanırız ?” için 19 Yorum

  1. sadettinpolat diyor ki:

    cok guzel bir makale. tesekkurler…

    yazi icerisinde gizli ozne olarak bahsedilmis olsada (com teknolojisi) musadenizle ben bu konuya biraz vurgu yapmak istiyorum.

    interfacelerin bana gore en onemli ozelligi birbirinden bagimsiz yazilimcilarin gelistirmis oldugu modulleri , sistemleri , programlari kolay bir sekilde kullanabilmemizi saglamaktir. bu interface olayini kim neden cikartmis bilmiyorum ama aklima gelen en mantikli sebep olarak bunu goruyorum.

    ic dunyasinda tamamen kendine ozgu ve bagimsiz ama dis dunyaya karsi belirlenmis bazi standartlara ayak uydurmak. tipki bir televizyon kumandasi gibi. televizyon kumandasi ureten insanlar icin kumandanin tasarlanisi , devreleri , icinde kullanilan malzemeye kadar butun hersey kendi isteklerine baglidir. istedikleri sekilde tasarlayip bu kumandayi uretebilirler. dikkat etmeleri gereken tek sey kumandanin uzerindeki dugmelerin standartlara ya da piyasadaki diger kumandalara benzer olmasidir. bu saye de kumandayi satin alan insanlar kumandanin ic yapisini bilmeden kolay bir sekilde kumandayi kullanabileceklerdir.

    biz yazilimcilarda kendi dunyamizda bu modeli uygulayabilmek icin interfaceleri kullaniyoruz.

    x kisisi bir web servis yaziyor ve bunu bizim hizmetimize sunuyor. bu web servisi import ettigimizde bize bir arabirim (interface) sunuyor. benim arabirimim bu. bana bu sekilde bilgi gonderirsen ben de gerekli cevabi sana su sekilde gonderirim diyor. yada ms odbc diye bir sey cikartip bu teknolojinini arabirimi soyledir diyor. 3. parti urun gelistiriciler ise bu arabirime sadik kalip kendi veritabani suruculerini yaziyorlar. (firebird , mysql vb.) biz ise programimizda odbc arabirimini kullandigimizdan sadece connectin stringi degistirip kullandigimiz veritabanini degistirebiliyoruz.

    f klavye ile q klavye modunda bu kadar yazabildim. ustelik ictima saati de geldi :) yakalanmadan kacayim :)

  2. Tuğrul HELVACI diyor ki:

    :) Yorumuna teşekkürler sadettincim. Şafak kaç şafak ;)

  3. Veli BOZATLI diyor ki:

    Şahsım adına çok yararlı bir makale. Emeğinize sağlık Tuğrul Bey.

    • Tuğrul HELVACI diyor ki:

      Sizler yorum yazdıkça ben daha çok makale yazarım Allah’ın izni ile. Yararlı olabiliyor isem ne mutlu bana.

  4. Sinan BARAN diyor ki:

    Merhaba Tuğrul hocam,
    Makalelerini okumak gerçekten büyük bir zevk…Her sözde bilgi her sözde anlam ellerine emeğine sağlık hocam.

    • Tuğrul HELVACI diyor ki:

      Merhaba Sinancım, sağolasın yorumun için. Senin de methini duyuyoruz Bilge Adam’da beğenilen bir eğitimci olma yolunda ilerliyormuşsun. JQuery seminerine gelip seni dinleyecektim ama nasip olmadı. İnşallah bir başka seminerinde seni de izleyeceğim. ;)

  5. Sinan BARAN diyor ki:

    Estafurullah hocam,Yazılımda sizin bakış açınızın büyük payı var, gerçekten okunası ve örnek alınması gereken bir çok şey var sizde ve bunu blog sitenizede yansıtmış olduğunuz için bir çok insan şanslı(bunlardan birtanesi ‘de benim)

  6. Sinan BARAN diyor ki:

    Estafurullah hocam :)

  7. Mikail Abdullah diyor ki:

    Hocam ellerine ve sabrına sağlık. Çok iyi ve detaylı anlatmışsın. Ben de eğer bir şey anlatacaksam derinlemesine anlatayım diye düşünüyorum. Aslında ilk defa Interface hakkında C#’ı öğrenirken duymuştum ama pek iyi anlayamamıştım. Ama şimdi sayende 80% anladım.Her halde geri kalan 20%yi de pratikte anlarım. Ben aslında Threading’le ilgili siteye bakmıştım ama görüyorum çok faydalı ve değerli bilgiler var. Yazıların devamını istirham ederiz.

  8. mert şahin diyor ki:

    tthread ile

    islem= class(TThread, IThreadInterface)

    tarzı bir uygulama yapabilir miyiz bir fikriniz var mı?

  9. Mustafa Özpolat diyor ki:

    Ellerine sağlık Hocam. Allah Razı olsun.

  10. Gökhan GÜMÜŞ diyor ki:

    Üstadım; gerçekten çok güzel makale olmuş. Uzun zamandır böyle güzel bir makale okumamıştım.

    tebrik ederim.

  11. kişisel blog diyor ki:

    Müthiş bir makale olmuş! Elinize emeğinize sağlık

  12. selman diyor ki:

    yine güzel bir makale, teşekkürler.

  13. Ahmet diyor ki:

    Ellerinize ve yüreğinize sağlık Allah razı olsun.

  14. Gürol diyor ki:

    Selamlar Tuğrul Bey;
    Yazınızı okuduktan sonra size bir konuda danışma ihtiyacı hissettim. Biz bir süredir bir proje üzerinde çalışıyoruz. Kısaca özetlemek gerekirse C++ yazılmış bir dll yapıyı programımıza ekledik. Bu yapı sistem haberleşmesi için kullanılıyor ve tcpip üzerinde kanallar açarak veri transferini yapıyor. dll event fonksiyonlarını kullanırken cdecl ile çağırıyoruz dolayısı ile bu fonksiyonları herhangi bir bileşene bağlayamıyoruz. Yapmaya çalıştığımız iş çok detaylı olduğu için burada daha mantıklı gelecek bir konuya benzeterek sorunumu iletmek istiyorum. Bir bileşenimiz olsun bu bileşenin içinde yerleşik inputpin, outputpin alanları olsun. Bunlara değer yazıp okuyabileyim. Bir başka bileşenim daha olsun. Onun inputpin özelliğine baktığımda form üzerindeki diğer bileşenlerin outputpin listesini combobox üzerinde göreyim istediğimi seçeyim. Değerler değiştiğinde ilgili bileşen ilgili işi yapsın (örneğin ekranda görüntülesin) ve ben bu pin’lere bileşenlerin dışından da erişebileyim örneğin haberleşme kanalım bir değeri yakaladığında ilgili bileşene bu değeri göndersin (mitov software’de bileşenlerin çalışma mantığı buna benzer bir yapıda) Kısacası bileşenin içinden bir alanı referans vererek başka bir bileşen içinden görmek ve kullanmak istiyorum. Sizce yapı nasıl olmalı?. Teşekkürler. Kolay gelsin..

  15. Tuğrul HELVACI diyor ki:

    Tabii sistemi ve ne yaptığını tam bilemiyorum ama sizin yerinizde olsam, ilgili DLL’i onunla haberleşen, o DLL’e bilgi gönderip alan bir ata sınıf ile sarmalardım. Bu sayede DLL ile haberleşme esnasında kritik olabilecek işlemler için atomik korumayı merkezi bir yerden yapabilirdim. DLL ‘ile haberleşmede özel sınıflara ihtiyacım varsa da ilgili ata sınıftan yeni sınıflar türetip, ata sınıfta virtual olarak tanımlamış olduğum metodları ezerdim.

Yorum Yazın