Loose Coupling etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Loose Coupling etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

2 Eylül 2013 Pazartesi

Rtti Gevşek Unitler :)

Bir projedeki unit'lerin uses bloglarına baktığımızda bir sürü unit ismi görürüz.

Bunların bir çoğu da projeye bizim eklediğimiz unit dosyalarıdır.

Ve hepsi birbirine bağlıdır.Bu bağımlığı en aza indirmenin yollarından birisi de ,reflection kullanmaktır. :)

Aşağıda yazacağım rttiFormActivator methodu ile bir "unitismi.formsınıfadı" vererek formu oluşturmak için kullanabiliriz.

Bu method kullanıma göre özelleştirebilir.

function  rttiFormActivator(typeQualifiedName : string; isModal:Boolean=false) : TValue;
var
 context      : TRttiContext;
 rttTyp       : TRttiType;
 instanceType : TRttiInstanceType;
 instance     : TValue;
begin

 try
   context   := TRttiContext.Create();
   rttTyp    := context.FindType(typeQualifiedName);

   instanceType := rttTyp.AsInstance;
   instance     := instanceType.GetMethod('Create').Invoke(instanceType.MetaclassType,[nil]) ;

   Result := instance;

   case isModal of
      true  :   instanceType.GetMethod('ShowModal').Invoke(instance,[]);
      false :   instanceType.GetMethod('Show').Invoke(instance,[]);
   end;

 except on E: Exception do
   raise Exception.Create(typeQualifiedName+ ' Tipi register edilmemiş !');
 end;

end;
Unit1.pas dosyanın uses bloğuna kullanmak istediğimiz herhangi bir unit içersindeki formun unitini tanımlamadan direk aşağıdaki kullanılabilecektir.
procedure TForm1.btnShowForm2Click(Sender: TObject);
begin
  rttiFormActivator('Unit2.TForm2',true);
end;


procedure TForm1.btnShowForm3Click(Sender: TObject);
begin
  rttiFormActivator('Unit3.TForm3');
end;
unit1.pas'ın uses bloğuna unit2,unit3 eklemeyerek,bu unitlere bağlı olmasını engellemiş ve dinamik bir kullanım elde etmiş olduk. Yanlız şöyle önemli bir durum var. Kullanacağınız sınıflarını register etmediğiniz zaman rtti amca findType methodu ile ilgili verdiğiniz tipi bulamıyor. o yüzden sınıfları bulunduğu unit içinde register ederek,findType methodu ile bulmasını sağlayabiliriz.
initialization
 RegisterClass(TForm2);

finalization
 UnRegisterClass(TForm2);


Kaynak Kod : https://github.com/ismailkocacan/Rtti-Loose-Coupling-Units

iyi çalışmalar.

1 Eylül 2012 Cumartesi

Dependency Injection Giriş

{
  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 da var.
Bir daha ki sefere...


Kaynak kodları(XE2) buradan indirebilirsiniz

Kaynaklar : 
http://martinfowler.com/articles/injection.html
http://www.kodcu.com/2011/04/inversion-of-control-ioc-nedir/