23 Ocak 2018 Salı

Delphi Sınıf Yapısı - Sanal (Virtual) metotlar

Sanal (virtual) Metotlar

Delphi'de bir metot virtual olarak belirtilmediğinde o metot sanal olmaz statik olur. Peki virtual'ın farkı nedir? Bunu şu şekilde anlatabiliriz;
Bir ata sınıfın tanımlanmış bir sanal metodunu onun çocuğu olan bir sınıfta ezdiğimiz (override) zaman, ata sınıfı göstererek bu metodu çalıştırdığımızda, çocuk sınıfın metodu çalışacaktır.

Örnekleyelim;
uses
  System.SysUtils,
  System.Contnrs;

type
  TMeyve = class
  strict private
    FMeyveCinsi : string;
    FMeyveMiktari : string;
  public
    property MeyveCinsi : string
                 read FMeyveCinsi write FMeyveCinsi;
    property MeyveMiktari: string
                 read FMeyveMiktari write FMeyveMiktari;
    procedure Topla; virtual;
  end;

  TAgaclar = class
  private
    FMeyveAgaclari : TObjectList;
  public
    constructor Create;
    destructor Destroy; override;
    procedure AgacEkle(agac : TMeyve);
    procedure MeyveleriTopla;
  end;

  TElma = class(TMeyve)
    constructor Create;
    procedure Topla; override;
  end;

  TArmut = class(TMeyve)
    constructor Create;
    procedure Topla;
  end;



Burada önce bir TMeyve ata sınıfı tanımlıyoruz, bundan türetilmiş TElma ve TArmut meyveleri var. Bunların ikisinin de Create yapılandırıcısı (constructor) ve Topla metodu var. Ancak TElma'nın Topla metodu öncekini ezmiş TArmut'un Topla metodu bilindik statik metot. Şimdi bunların kodlarını da şu şekilde yazarsak,

 { TMeyve }

procedure TMeyve.Topla;
begin
  Writeln(MeyveCinsi+' toplanmadı');
end;

{ TAgac }

procedure TAgaclar.AgacEkle(agac: TMeyve);
begin
  FMeyveAgaclari.Add(agac);
end;

constructor TAgaclar.Create;
begin
  FMeyveAgaclari := TObjectList.Create;
end;

destructor TAgaclar.Destroy;
begin
  FMeyveAgaclari.Destroy;
  inherited;
end;

procedure TAgaclar.MeyveleriTopla;
var
  meyve: TMeyve;
begin
  for meyve in FMeyveAgaclari do
  begin
    meyve.Topla;
  end;
end;

{ TElma }

constructor TElma.Create;
begin
  MeyveCinsi := 'Elma';
  MeyveMiktari := '15 adet';
end;

procedure TElma.Topla;
begin
  Writeln(MeyveMiktari+' '+MeyveCinsi+' toplandı...');
end;

{ TArmut }

constructor TArmut.Create;
begin
  MeyveCinsi := 'Armut';
  MeyveMiktari := '81 adet';
end;

procedure TArmut.Topla;
begin
  Writeln(MeyveMiktari+' '+MeyveCinsi+' toplandı...');
end;

var
  agaclar : TAgaclar;
begin
  try
    agaclar := TAgaclar.Create;
    try
      agaclar.AgacEkle(TElma.Create);
      agaclar.AgacEkle(TArmut.Create);
      agaclar.MeyveleriTopla;
      Readln;
    finally
      agaclar.Free;     
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Bu örnekte agaclar.MeyveleriTopla metodunda her bir meyvenin Topla metodunu çağırdığımızda elma'nın Topla metodu çalışır ama armutun Topla metodu çalışmaz. Onun yerine TMeyve sınıfının Topla metodu çalışır ve o da armutun toplanamaması olacaktır.

Burada meyve listesini tutmak için TObjectList (geleneksel list, generic değil) kullandım. TList te kullanabilirdim. Ancak TList pointer listesi için kullanıldığından dolayı listenin işi bitince free yapıldığında nesneleri free yapamaz. TObjectList ise free edildiğinde listedeki tüm nesneleri de yok edecektir.

Bu örnek konsol uygulaması olarak çalıştırılabilir. Bir console uygulaması oluşturup içindekiler yerine bunları kopyalarsanız test edebilirsiniz.


Not : Bu örnekler Delphi 10.1 Berlin Delphi 10.2 Tokio için denenmiştir.



1 yorum: