OOP Delphi

Nesne Tabanlı Programlama (1) 8 Kasım 2002 06:29
ceoworks
Component Writers Guide “OBJECT ORIENTED PROGRAMMING WITH DELPHI”
Konu ile ilgili kendi örneklerim ve bazı ek bilgileri içeren dökümana kısa bir zaman sonra “Nesne Tabanlı Programlama – Örnekler” başlığı altında ulaşabilirsiniz.
--- DELPHI ve NESNE TABANLI PROGRAMLAMA(OOP) ---
Bileşen yazarı bir delphi programcısı olarak yeni bir bileşen yazarken, uygulama programcılarının kullanmaya ihtiyaç duymadığı sınıf kavramları ile ilgilenmek durumunda kalırsınız. Yazılan bileşenin alt seviyedeki bazı özelliklerini, bu bileşeni kullanacak programcılardan gizlemeniz gerekir.
Çok yönlü ve yeniden kullanılabilir bir bileşen yazmak için öncelikle;
- Yazılacak bileşene uygun temel bileşen seçilir
- Bileşeni kullanacak programcı tarafından, sadece ihtiyaç duyulabilecek özellik/metodlar kullanıma açılır.
Yeni bileşenler yazmaya başlamadan önce aşağıdaki konulara aşina olmanız gerekmektedir. Bunlar aynı zamanda nesne tabanlı programlama ile de yakından ilişkilidir.
- yeni sınıf tanımlama
- ata, torun hiyerarşisi ve sınıflar
- sınıflara erişimin kontrolü
- metodlara gönderme yapmak
- sınıf üyelerini soyutlama
- sınıflar ve pointerlar
--- Yeni Sınıflar Tanımlama ---
Bileşen yazarı ile uygulama programcısı arasındaki fark; uygulama programcısı mevcut sınıfların örnekleri ile çalışırken, bileşen yazarının yeni sınıflar oluşturmasıdır.
Her sınıf aslında bir tiptir. Bu terminolojiyi kullanmasanız bile, bir programcı olarak herzaman tip ve örneklerle çalışırsınız. Mesela integer tipinden bir değişken tanımlarken.. Sınıflar genellikle, bildiğimiz temel veri tiplerinden daha karmaşıktır ancak aynı şekilde çalışırlar; aynı tipin örneklerine değişik değerler atayarak farklı işler yaptırabilirsiniz.
Örneğin iptal ve tamam başlıklı iki tane buton içeren bir form oluşturmaya sık sık gereksinim duyulur. Herbir buton aynı TButton sınıfının bir örneğidir fakat Caption özelliklerine ve OnClick olaylarına ayrı değerler atayarak, birbirinden farklı davranan butonlar yapabilirsiniz.
--- Yeni Sınıflar Türetme ---
Yeni bir sınıf türetmek için iki sebep vardır:
- Mevut sınıfın, yeni sınıfta da bulunması istediğimiz özelliklerini tekrar yazmaktan kurtulmak
- Sınıfa yeni yetenekler kazandırmak
Her iki durumda da amaç yeniden kullanılabilir nesneler oluşturmaktır. Eğer bileşenlerinizi yeniden kullanılabilirliği gözönüne alarak dizayn ederseniz, aynı şeyleri yeniden yazmaktan kurtulup zamandan kazanabilirsiniz. Sınıflarınıza, uygun hazır değerler verin, fakat bu değerlerin değiştirilebilmelerine de izin verin.
--- Sınıfların Hazır Değerlerini Tekrardan Kurtarmak ---
Pekçok programcı tekrardan kaçınmaya çalışır. Bu şekilde defalarca aynı kod satırlarını yazdığınızı farkederseniz, bu kodu bir prosedür yada fonksiyon içerisine yerleştirin yada birçok programda kullanabileceğiniz bir rutin kütüphanesi oluşturun. Aynı mantık nesneler için de geçerlidir. Eğer bir nesnenin sürekli aynı özelliklerini değiştirdiğinizi yada aynı metod çağrıları ile uğraştığınızı farkederseniz, bu özellikleri kendisinde hazır olarak bulunduran bir nesne oluşturmayı düşünebilirsiniz.
Örneğin her uygulamamızda bazı işlemler için gerekli olan bir dialog kutusu oluşturduğunuzu farzedelim. Her seferinde bu dialog kutusunu oluşturmak zor olmasa da, gerekli de değildir. Dialog kutusu bir kereliğine dizayn edilir, özellikleri belirlenir ve bileşen paletinde ilişkili olduğu bölüme kurulur. Dialog kutusunu yeniden kullanılabilir bir bileşene çevirerek sadece tekrar eden işlemlerden kurtulmuş olmazsınız, aynı zamanda her dialog kutusu oluşturulduğundaki hata oluşma olasılığını da bertaraf etmiş olursunuz.
Eğer mevcut bileşenin sadece object inspectordan da görüntülenebilen, yayımlanmış özelliklerini değiştirmek, yada bileşen/bileşen grubunun belirli olay yöneticilerini kaydetmek isterseniz bunu bileşen şablonları oluşturarak daha kolay bir şekilde gerçekleştirebilirsiniz.
--- Sınıfa Yeni Yetenekler Kazandırmak ---
Yeni bir bileşenler oluşturmanın temel nedeni, bileşene mevcut halinde bulunmayan özellikler eklemektir. Bunu yaparken, ya mevcut bileşenden yada daha temel özet sınıftan(TComponent, TControl..vb) türetme yapılır.
Yeni bileşeninizi, kullanmayı düşündüğünüz temel özelliklerin çoğunu içeren bir sınıftan türetin. Sınıfa yeni yetenekler kazandırabilirsiniz fakat mevcut özelliklerini çıkartamazsınız. Bu nedenle mevcut sınıf, sizin sınıfınızda kullanmak istemediğiniz bazı özellikler içeriyorsa, sınıfınızı bu sınıfın hiyerarşideki bir üst ata sınıfından türetmeniz gerekir.
Örneğin list box bileşenine yeni özellikler eklemek isterseniz, bileşeninizi TListBox sınıfından türetebilirsiniz. Bununla birlikte, eğer standart list box’a bazı özellikler eklemek fakat bazı özelliklerini de devre dışı bırakmak isterseniz, nesnenizi TListBox’ın ata sınıfı olan TCustomListBox’dan türetmeniz gerekir. Böylelikle list box’ın sadece istediğiniz özelliklerini yeniden tasarlayabilir ve yeni özellikler ekleyebilirsiniz.
--- Yeni Nesne Sınıfları Tanımlama ---
Standart bileşenlere ek olarak Delphi, yeni nesneler türetmek için birçok özet sınıf da sağlar. Kendi bileşenlerinizi oluştururken seçeceğiniz başlangıç noktası size başlayabileceğiniz sınıfı gösterebilir.
Yeni bir bileşen oluştururken; sınıf tanımını bileşenin unit dosyasının içine ekleyin.
Buradaki basit bir grafik bileşenin tanımı:
type
TSampleShape = class(TgraphicControl)
end;
Tamamlanmış bir bileşen tanımı genellikle end’den önce; özellik, olay ve metod tanımları içerir. Fakat yukarıdaki gibi bir tanım da geçerlidir ve ek özellikler için de bir başlangıç noktası sağlar.
--- Ata, Torun ve Sınıf Hiyerarşisi ---
Uygulama programcıları her kontrolün form üzerindeki koordinatlarını belirleyen Top ve Left özellikleri olduğunu kabul ederler. Onlar için tüm bileşenlerin bu özelliklerini ortak ataları olan TControl’den alması bir önem taşımaz. Siz bir bileşen oluştururken bununla birlikte bileşeninizi hangi sınıftan türeteceğinizi ve hangi özelliklerin bu ata sınıftan devralındığını da bilmek zorundasınız. Böylelikle zaten devralınmış özellikleri yeniden oluşturmanıza gerek kalmaz.
Bileşenin türetildiği sınıfa, o bileşenin ata sınıfı denir. Türetilen her bileşen bir ata sınıftan türer ve o da bir ata sınıftan.. Bu hiyerarşi TControl sınıfına kadar sürer. Türettiğiniz her sınıf bu ata sınıfların torunlarıdır.
Tüm ata-torun ilişkilerinin hepsi birden sınıfların uygulama içerisinde oluşturduğu hiyerarşidir. Sınıfınız ata sınıfından tüm özelliklerini devraldığı ve yeni özellikler, metodlar eklediği yada mevcut sınıfın bazı özelliklerini yeniden tanımladığı sürece, hiyerarşideki herbir jenerasyon, atasından daha fazlasını içerir.
Ata sınıf olarak hazır bir sınıf belirlemediyseniz, Delphi bileşeninizi, nesne hiyerarşisindeki en temel ata sınıf olan TObject’den türetir.
Hangi bileşenden türetme yapacağınızı seçmek için genel kural basittir: yeni bileşeninizde bulundurmayı istediğiniz özelliklerden mümkün olduğunca çoğunu içeren bileşeni seçin, fakat bu bileşen, sizin bileşeninizin içermesini istemediğiniz özelliklerden hiçbirini barındırmasın. Her zaman bileşenlere yeni şeyler ekleyebilirsiniz, fakat çıkaramazsınız..
Nesne Tabanlı Programlama (2) 8 Kasım 2002 06:36
ceoworks
--- Erişim Kontrolü ---
Özellik ve metodlarda görünürlük olarak da bilinen, 5 seviye erişim kontrolü vardır. Görünürlük, hangi kodun, sınıfın hangi kısımlarına erişebileceğini belirler. Görünürlüğü belirterek, bileşenlerinize arayüz tasarlarsınız.
Aşağıdaki tablo en kısıtlanmışdan, en ulaşılabilire doğru görünürlük seviyelerini gösterir :
Görünürlük --- Anlamı --- Ne için kullanılır ?
----------------------------------------------
1.)Private --- Sadece, sınıfın tanımlanmış olduğu unit’in içerisindeki koddan ulaşılabilir --- Detayları gizlemek
2.)Protected --- Sınıf yada bu sınıfın torunlarının tanımlandığı unit(lerin) içerisindeki kodlardan ulaşılabilir --- Bileşen yazarlarını arayüzü tasarlamak
3.)Public --- Tüm kodlardan erişilebilir --- Çalışma zamanı arayüzü tasarlamak
4.)Automated --- Tüm kodlardan erişilebilir. Otomasyon tip bilgisi üretilmiştir --- Sadece OLE otomasyonu
5.)Published --- Tüm kodlardan ve Object Inspector’dan erişilebilir --- Tasarım zamanı arayüzünü tasarlamak
Öğenin, sadece sınıfın tanımlandığı yerde kullanılabilir olmasını istiyorsanız, Private olarak tanımlayın; sadece o sınıfın içerisinde ve torunlarında kullanılabilir olmasını istiyorsanız Protected olarak tanımlayın. Hatırlayın, eğer bir öğe, unit dosyasının içerisindeki herhangi bir yerde kullanılabilir ise, bu dosyanın her yerinde kullanılabilirdir. Böylelikle, eğer aynı unitin içerisinde iki sınıf tanımladıysanız, herbir sınıf birbirinin private metodlarına ulaşabilir. Ata sınıfın dışındaki bir unitten, bu ata sınıftan bir sınıf türettiğinizde; yeni unitdeki tüm sınıflar, ata sınıfın protected metodlarına ulaşabilecektir.
--- Detayları Gizlemek ---
Sınıfın bir kısmını private olarak tanımlamak, bu kısmı, sınıfın unit dosyasının dışından görünmez yapar. Tanımlamayı içeren unit içerisinde, kod bu kısıma sanki public olarak tanımlanmış gibi ulaşabilir.
Buradaki örnek, belirlediğiniz bölümü nasıl private olarak tanımlayıp, uygulama programcılarından gizleyebileceğinizi gösterir. Listelenenler iki form unitini gösteriyor. Herbir form private kısıma değer atayan, kendi OnClick olayına sahiptir. Derleyici private kısıma yapılan atamalara, sadece formun tanımlandığı yerden izin verir.
unit HideInfo;
interface
uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs;
type
TSecretForm = class(TForm) { yeni form tanımla }
procedure FormCreate(Sender: TObject);
private { private kısmı tanımla }
FSecretCode: Integer; { private bir alan tanımla }
end;
var
SecretForm: TSecretForm;
implementation
procedure TSecretForm.FormCreate(Sender: TObject);
begin
FSecretCode := 42; { bu doğru olarak derlenir }
end;
end. { unitin sonu }
unit TestHide; { bu ana form dosyası }
interface
uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs,
HideInfo; { TSecretForm ile uniti kullan }
type
TTestForm = class(TForm)
procedure FormCreate(Sender: TObject);
end;
var
TestForm: TTestForm;
implementation
procedure TTestForm.FormCreate(Sender: TObject);
begin
SecretForm.FSecretCode := 13; {derleyici "Field identifier expected" hatası ile durur }
end;
end. { unitin sonu }
HideInfo unitini kullanan program hernekadar TSecretForm tipindeki nesnelere ulaşabilse de, FSecretCode kısmına bu nesnelerin hiçbirinden ulaşamaz.
--- Bileşen Yazarı Arayüzü Tanımlama ---
Sınıfın bir bölümünü protected olarak tanımlamak, bu kısmı, sınıfın sadece kendisine ve torun sınıflarına(ve unit doyalarını paylaşan diğer sınıflara) görünür yapar.
Sınıflara bileşen yazarı arayüzü tasarlamak için protected tanımını kullanabilirsiniz. Uygulama unitleri protected bölümlere ulaşamaz fakat türetilmiş sınıflar ulaşabilir. Bu demek oluyor ki; bileşen yazarları, detayları uygulama programcılarına görünür yapmadan, sınıfın çalışma şeklini değiştirebilirler.
--- Çalışma Zamanı Arayüzü Tanımlama ---
Sınıfın bir bölümünü public olarak tanımlamak, bu kısmı, sınıfa ulaşımı olan tüm kodlara görünür yapar.
Public bölümler çalışma zamanında tüm kodlarda kullanılabilir, bu yüzden sınıfların public kısımlarına onun çalışma zamanı arayüzü denir. Çalışma zamanı arayüzü, çalışma zamanındaki girdiye bağlı özellikler gibi dizayn zamanı için uygun yada anlamlı olmayan maddeler için kullanışlıdır. Uygulama geliştiriciler için tasarladığınız metodların public olması gereklidir.
Buradaki örnek, bileşenin çalışma zamanı arayüzü olarak tasarlanmış iki tane sadece okunabilir özelliği gösteriyor:
type
TSampleComponent = class(TComponent)
private
FTempCelsius: Integer; { uygulama detayları private }
function GetTempFahrenheit: Integer;
public
property TempCelsius: Integer read FTempCelsius; { özellikler public }
property TempFahrenheit: Integer read GetTempFahrenheit;
end;
...
function TSampleComponent.GetTempFahrenheit: Integer;
begin
Result := FTempCelsius * 9 div 5 + 32;
end;
--- Tasarım Zamanı Arayüzün Oluşturulması ---
Sınıfın bir bölümünü published olarak tanımlamak, bu kısmı public yapar ve çalışma zamanı tip bilgisini oluşturur. Diğerleri ile birlikte çalışma zamanı tip bilgisi, Object Inspector’ın özellik ve olaylara ulaşamasına da izin verir.
Published olarak tanımlanmış bölümler, object inspector’dan da görüntülenebilir ve sınıfın tasarım zamanı arayüzünü oluştururlar. Tasarım zamanı arayüzü, uygulama geliştiricinin dizayn zamanında değiştirmek isteyebileceği tüm yönleri içermeli, fakat çalışma zamanı ortamı ile ilgili belli bilgilere bağlı olan tüm özellikleri de uzak tutmalıdır.
Uygulama geliştiriciler doğrudan değer atayamayacağından dolayı, sadece okunabilir özellikler, tasarım zamanı arayüzünün bir parçası olamaz. Bu nedenle sadece okunabilir özellikler, published yerine public olarak tanımlanmalıdır.
Burada published olarak tanımlanmış Temperature özelliği görülmektedir. Böyle bir tanımlama ile bu özellik Object Inspector’dan da görünür olmuş olur.
type
TSampleComponent = class(TComponent)
private
FTemperature: Integer; { detaylar private olarak tanımlanmış }
published
property Temperature: Integer read FTemperature write FTemperature;{ yazılabilir ! }
end;
Nesne Tabanlı Programlama (3)
--- Metodlara Gönderme Yapmak ---
Gönderme, bir metod çağrısı ile karşılaştığında, metoda başvurulması gereken yerde, programın belirlediği yola başvurur. Metodu çağıran kod, diğer prosedür yada fonksiyon çağrılarındakine benzer. Fakat sınıfların farklı metod çağrı şekilleri vardır.
Üç tip metod çağrısı vardır
- Static
- Virtual
- Dynamic
--- Statik Metodlar ---
Siz tanımlarken başka türlü belirtmedikçe tüm metodlar statiktir. Statik metodlar, normal bir prosedür yada fonksiyon gibi çalışırlar. Derleyici, metodun tam adresini belirler ve derleme zamanında metoda bağlantı sağlar.
Statik metodların temel avantajı, bu metodlara çok hızlı gönderme yapılabilmesidir. Çünkü derleyici metodun tam adresini belirler ve doğrudan bağlantı sağlar. Virtual ve dynamic metodlar ise tam tersi olarak, çalışma zamanında metodun adresine bakarak, daha fazla zaman gerektiren, doğrudan olmayan bağlantı kullanırlar.
Static metodlar bir torun sınıftan türetildiğinde kalıtsal değişikliğe uğramazlar. Statik metod içeren bir sınıf tanımlar, sonra da bundan yeni bir sınıf türetirseniz, türetilmiş sınıf tam olarak aynı adresteki aynı metodu paylaşır. Bu static metodları ezemeyeceğiniz anlamına gelir; statik metodlar hangi sınıftan çağrıldığını umursamaksızın, herzaman aynı şeyleri yaparlar. Eğer türetilmiş sınıf içerisinde statik metod olarak aynı isimde, ata sınıftaki gibi bir metod tanımlarsanız, yeni metod türetilmiş sınıftaki kalıtsal olanın yerini kolaylıkla alır.
--- Bir Static Metod Örneği ---
Aşağıdaki kodda ilk bileşen iki adet static metod tanımlar. İkincisi, ilk bileşenden miras kalan metodların yerini alacak, aynı isimlerde iki static metod tanımlar.
type
TFirstComponent = class(TComponent)
procedure Move;
procedure Flash;
end;
TSecondComponent = class(TFirstComponent)
procedure Move; { aynı tanımlamaya rağmen, miras kalan metoddan farklı }
function Flash(HowOften: Integer): Integer; { bir de bu farklı}
end;
--- Virtual Metodlar ---
Virtual metodlar, static metodlara göre daha karmaşık ve daha esnek gönderme mekanizmasına sahiptir. Bir virtual method torun sınıfta yeniden tanımlanabilir fakat yine de ata sınıftan çağrılır. Virtual metodların adresleri derleme zamanında tanımlanmaz; bunun yerine, metodun tanımlandığı nesne adresi çalışma zamanında arar.
Virtual bir metod oluşturmak için, metod tanımına virtual direktifini ekleyin. Virtual direktifi, nesnenin virtual metod tablosunda yada VMT’de(nesne tipindeki tüm virtual metodların adreslerini tutar) bir kayıt oluşturur.
Mevcut sınıftan yeni bir sınıf tanımladığınızda, yeni sınıf ata sınıfının VMT’sindeki tüm kayıtlar ile birlikte, yeni sınıfın virtual metodlarının da bulunduğu kendi VMT’sini alır.
--- Metodların Ezilmesi ---
Metodları ezmek, bu metodu başka birisi ile değiştirmekten çok, genişletmek yada rütuş yapmak anlamına gelir.
Torun sınıfta bir metod ezmek için, metod tanımının sonuna override direktifi konulmalıdır.
Eğer;
- Metod ata sınıfta mevcut değilse.
- Aynı isimdeki ata metod static ise.
- Tanımlama özdeş metodunkinden farklı olursa(parametrelerin sayısı ve tipi farklı olursa..)
metod ezilmesi derleme hatasına neden olur.
Aşağıdaki kod iki temel bileşenin tanımını içerir. İlki, herbiri farklı şekilde gönderme yapan üç metod tanımlar. Diğeri ilkinden türetilmiştir, static metodu bir başkası ile değiştirir ve virtual metodları ezer.
type
TFirstComponent = class(TCustomControl)
procedure Move; { static metod }
procedure Flash; virtual; { virtual metod }
procedure Beep; dynamic; { dynamic virtual metod }
end;
TSecondComponent = class(TFirstComponent)
procedure Move; { yeni metod tanımlar }
procedure Flash; override; { kalıtsal metodu ezer }
procedure Beep; override; { kalıtsal metodu ezer }
end;
--- Dynamic Metodlar ---
Dinamik metodlar, biraz farklı gönderme mekanizması olan virtual metodlardır. Çünkü dinamik metodların nesnenin virtual metod tablosunda kayıtları yoktur, nesnelerin tükettiği hafızanın miktarını düşürebilirler. Dinamik metoda gönderme yapmak, bilinen virtual metodlara gönderme yapmaktan oldukça daha yavaştır. Metod sık sık çağrılıyorsa yada zamanın önemli olduğu uygulamalarda dynamic tanımlama yapmaktansa, muhtemelen virtual olarak tanımlamanız gerekir.
Nesneler dinamik metodlarının adreslerini saklamalıdır. Fakat virtual metod tablosundaki kayıtları almak yerine, dinamik metodlar bağımsız olarak listelenir. Dinamik metod listesi, sadece belirili sınıflar tarafından tanıtılmış yada ezilmiş metodlar için kayıtlar içerirler. (Virtual metod tablosu, farklı olarak, tüm nesnelerin kalıtsal olarak gelen ve tanıtılmış virtual metodlarını içerir.) Miras kalmış dinamik metodlara, sınıf hiyerarşi ağacı doğrultusunda geriye doğru gidilip, herbir atanın dinamik metod listesi aranarak atama yapılır.
Metodu dinamik yapmak için, dynamic direktifi metod tanımının sonuna eklenir.
--- Soyut Sınıf Üyeleri ---
Metod ata sınıfında abstract olarak tanımlandığında, herbir torun sınıfta kullanmadan önce onu yeniden tanımlayarak ve tamamlayarak kaplamanız gerekir. Delphi soyut üyeler içeren sınıfların örneklerini oluşturamaz. Sınıfın kalıtsal parçalarının kaplanması ile ilgili daha fazla bilgi için Delphi Component Writers Guide’daki “Özelliklerin Oluşturulması” ve “Metodların Oluşturulması” kısımlarına bakınız.
--- Sınıflar ve Pointerlar ---
Her sınıf(ve o nedenle her bileşen de) aslında bir pointerdır. Derleyici sizin için otomatik olarak sınıf pointerları gönderir, böylece çoğu zaman bunun hakkında düşünmenize gerek kalmaz. Sınıfın pointerlar olarak durumu, bir sınıfı parametre olarak geçerken önemli olmaya başlar. Genel olarak sınıfları bir referans yerine değer olarak geçmelisiniz. Sebep şudur; sınıflar zaten referans olan pointerlardır. Sınıfı referans olarak geçmek, bir referansı referans olarak göstermekle aynı anlamdadır.
--- Çeviri’de Kullanılan Türkçe Terimler ve Karşılıkları ---
bileşen yazarı = component writer
bileşen = component
uygulama programcısı = application developer
ata = ancestor
nesne Yönelimli Programlama = Object Oriented Programming (OOP)
sınıf = class
torun = descendant
soyut, özet = abstract
özellik = property
metod = method
tip = type
yeniden kullanılabilirlik = reusable
VMT = Virtual Method Table
nesne = object
yayımlanmış = published
türetmek = deriving
arayüz = interface
çalışma zamanı = runtime
ezmek = override
kayıt = record