Kod: Tümünü seç
{
Inversion of Control
Dependency injection at Delphi for Loose Coupling
İsmail KOCACAN 2012
}
unit uMain;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs;
type
IDocument = interface
// GUID oluşturmak için kısayol : Ctrl+Shift+G
['{44E273D9-3C17-4821-B791-31CDDCE6C6D8}']
procedure Parse;
end;
TBaseDocument = class abstract(TInterfacedObject)
private
FPath: string;
FContent: string;
public
property Path: string read FPath write FPath;
property Content: string read FContent write FContent;
end;
TXMLDocument = class(TBaseDocument, IDocument)
public
procedure Parse;
end;
TJSONDocument = class(TBaseDocument, IDocument)
public
procedure Parse;
end;
TDocumentParser = class
private
FDocument: IDocument;
public
constructor Create(aDocument: IDocument);
procedure DoParse;
end;
TfrmMain = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
{ TXMLDocument }
procedure TXMLDocument.Parse;
begin
OutputDebugString('XML parseleme işlemi yapıldı.');
end;
{ TJSONDocument }
procedure TJSONDocument.Parse;
begin
OutputDebugString('JSON parseleme işlemi yapıldı.');
end;
{ TDocumentParser }
constructor TDocumentParser.Create(aDocument: IDocument);
begin
FDocument := aDocument;
end;
procedure TDocumentParser.DoParse;
begin
FDocument.Parse;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
var
axmldocument: TXMLDocument;
ajsondocument: TJSONDocument;
aParser: TDocumentParser;
begin
axmldocument := TXMLDocument.Create;
aParser := TDocumentParser.Create(axmldocument);
aParser.DoParse;
ajsondocument := TJSONDocument.Create;
aParser := TDocumentParser.Create(ajsondocument);
aParser.DoParse;
end;
end.
TBaseDocument taban sınıfını, "IInterface" arayüzünün üyelerini implemente etmiş,
ve "TObject" sınıfın tüm özelliklerini bünyesinde barındıran "TInterfacedObject" sınıfından kalıtıyoruz.
"TBaseDocument" sınıfını "TInterfacedObject" sınıfından kalıtmamızın sebebi ise ;
Üst sınıflarda(TXMLDocument,TJSONDocument) "IDocument" arayüzünü implemente ederken
"IInterface" arayüzünün fonksiyonlarını da bizden implemente ettirmek istemesidir.
"TBaseDocument" sınıfından kalıtarak oluşturduğumuz her yeni sınıfın,
"IInterface" arayüzünün methodlarını da, implemente ettirmek kullanışlı bir yöntem değil...
O Sebeble ; TBaseDocument taban sınıfımızı türetebileceğimiz,
"IIInterface" arayüzünün fonksiyonlarını implemente etmiş,
başka bir sınıf lazım.Bu ihtiyacı karşılayacak olan sınıfı kendimizde yazabilirdik fakat,
hali hazırda var olan "TInterfacedObject" ihtiyacımızı karşılıyor.
"TBaseDocument" sınıfını TInterfacedObject sınıfından kalıtmamızın sebebi de budur.
"TXMLDocument" ve "TJSONDocument" sınıflarını "TBaseDocument" sınıfından türetiyoruz.
Ve "IDocument" arayüzünün methodlarını implemente ettiriyoruz.
Nitekim her yeni TBilmemneDocument nesnesinin Parse procedure'sinin gövdesinde farklı işlemler olacaktır.
Dependency injection prensibinin "Setter" procedure'ler ve Constructor methodlar ile uygulanadığını okumuştum.
Tabiki bence sadece bunlarla sınırlı değil injection olayı...
Biz nesneleri dış dünyaya,o nesnenin üyeleri ile açtığımız için,bence sadece set edilebilen,
bir property üzerinden de injection yapılabilir diye düşünüyorum...
injection kısmı ile daha kullanışlı yazım şekilleri aklımda var ama onlar bir daha ki sefere...
Dependency injection prensibin uygulandığı kısım ise "TDocumentParser" sınıfının yapılandırıcısında.
constructor Create(aDocument: IDocument);
Bu şekilde yazmakla derleyiciye şunu demiş oluyoruz.
IDocument arayüzünde veya IDocument arayüzünü implemente etmiş bir sınıf, parametre olarak geçilebilir.
Bu şekilde yazmak tipden bağımsız olduğu için, daha esnek bir kodlama oluyor.Nasıl yani? Esnek derken ?
Yani IDocument arayüzünü implemente etmiş,TBaseDocument sınıfında türeyen ,yeni yeni sınıflar yazıp,
TDocumentParser sınıfın yapılandırıcında hiçbir değişiklik yapmadan,bu yeni sınıfları parametre olarak geçebiliriz.
Bu tipden bağımsız,soyutlama yöntemi ile yazım tarzıydı.
Şimdi birde soyutlamadan yazdığımızı düşünecek olursak;
constructor Create(aDocument: TXMLDocument); overload;
constructor Create(aDocument: TJSONDocument); overload;
Her yeni yazdığımız üst sınıfda(misal TBilmemneDocument) Parse methodunu çağırabilmemiz için,
constructor Create(aDocument: TBilmemneDocument); overload; gibi değişikliği veya başka bir yazım tarzını,
"TDocumentParser" sınıfında yapmamız gerekecekti.
Rtti ve Dependency Injection ile ilgili aklımda bir senaryo daha var.
Bir daha ki sefere...
Kaynaklar :
http://martinfowler.com/articles/injection.html
http://www.kodcu.com/2011/04/inversion- ... ioc-nedir/