Felony
یک شنبه 30 مهر 1391, 16:07 عصر
سلام ،
در اکثر موارد وقتي تصميم ميگيريم که برنامه رو به کلاس هاي مجزا تقسيم کنيم و هر کلاس رو در کتابخانه اي جدا پياده کنيم در بخش هايي نياز ميشه که از کلاسي در کلاس ديگه استفاده بشه ، در اين مواقع بارها شده که با پيغاه خطا Circular unit reference مواجه شديد ، اين پيغام زماني مشاهده ميشه که از دو کتابخانه داخل يکديگر استفاده کرده باشيم ، مثلا :
unit Unit1;
interface
uses Unit2;
implementation
end.
unit Unit1;
interface
uses Unit1;
implementation
end.
يکي از راه حل ها در اين مواقع استفاده از Class Helper ها هست ( البته راه حل هايي مثل جدا کردن ساختارهايي که زياد استفاده ميشن در يک کتابخانه جداگانه يا طراحي مجدد و اصولي کلاسي که نوشته شده قبل از اين راه حل پيشنهاد ميشه ! )
خوب فرض کنيد کتابخانه هايي مثل اين :
unit Dog;
interface
type
TDog = class
Name: String;
end;
implementation
end.
و اين داريم :
unit Master;
interface
type
TMaster = class
Name: String;
end;
implementation
end.
خوب حالا تو برنامه براي استفاده از اين دو برادر عزيز ازشون نمونه سازي ميکنيم :
uses
Dog, Master;
var
Dog: TDog;
Master: TMaster;
begin
Dog := TDog.Create;
Master := TMaster.Create;
try
Dog.Name := 'Bobby';
Master.Name := 'Joe';
finally
Dog.Free;
Master.Free;
end;
end;
حالا تصميم ميگيريم به کلاس TDog متدي با نام Master اضافه کنيم ( از نوع TMaster ) که مشخصات مربي اين سگ رو نگهداري کنه :
unit Dog;
interface
uses Master;
type
TDog = class
Name: String;
Master: TMaster;
end;
implementation
end.
تا اينجا همه چي آرومه ، من چقدر خوشحالم ... :چشمک:
خوب حالا نياز داريم تو کلاس مربي هم يک متد با نام Dog داشته باشيم ( از نوع TDog ) که مشخصات سگ اين مربي رو نگهداري کنه ، خوب کد زير رو مينويسم :
unit Master;
interface
uses
Dog;
type
TMaster = class
Name: String;
Dog: TDog;
end;
implementation
end.
خوب ؟! همونطور که میبینید با پیغام خطا Circular unit reference عملیات کامپایل متوقف میشه .
اولین چیزی که به ذهن میرسه این هست که متد Dog رو از نوع TObject در نظر بگیریم ، یعنی :
unit Master;
interface
type
TMaster = class
Name: String;
Dog: TObject;
end;
implementation
end.
کد زیر بدون مشکل کامپایل میشه ولی بدیش این هست که برای هر کاری باید Typecast های متعددی انجام بدید ، مثلا برای نمایش نام مربی یک سگ :
Writeln(TDog(Master.Dog).Name);
و حالا راه حلی با استفاده از Class Helper ها ؛
اوین قدم تعریف Dog به صورت یک فیلد Protected در کلاس TMaster هست ، یعنی کلاس TMaster رو به صورت زیر مینویسم :
unit Master;
interface
type
TMaster = class
protected
fDog: TObject;
public
Name: String;
end;
implementation
end.
حالا کافیه به وسیله Class Helper ها یک پراپرتی برای کلاس TMaster بنویسیم تا شئ Dog رو به صورت یک نمونه از کلاس TDog به ما تحویل بده :
unit MasterClassHelper;
uses
Dog, Master;
type
TMasterClassHelper = class helper for TMaster
private
procedure SetDog(Value: TDog);
function GetDog: TDog;
public
property Dog: TDog read GetDog write SetDog;
end;
function TMasterClassHelper.GetDog: TDog;
begin
Result := TDog(FDog); // cast the TObject(FDog) to TDog!
end;
procedure TMasterClassHelper.SetDog(Value: TDog);
begin
FDog := Value;
end;
و حالا تو برنامه خیلی راحت بدون هیچ Typecast ی میتونیم از کلاس TMaster و TDog استفاده کنیم :
Writeln(Master.Dog.Name);
Writeln(Dog.Master.Name);
حتی :
Writeln(Dog.Master.Dog.Master.Dog.Master.Dog.Name) ;
:چشمک: آقا جنبه داشته باش !
همچنین با use کردن کتابخانه MasterClassHelper در کتابخانه Master علاوه بر کلاس TMaster میتونید به کلاس TDog هم دسترسی داشته باشید :
Unit unit1;
type
TMaster = class
protected
FDog: TObject;
public
Name: String;
procedure RenameDog;
end;
implementation
uses
MasterClassHelper;
procedure TMaster.RenameDog;
begin
Dog.Name := 'Buddy';
end;
در آخر اگر مجبور به استفاده از این مطلب شدید بهتره یه جفت کتاب در مورد اصول برنامه نویسی شئ گرا بخونید و تجدید نظری در مورد طراحی کلاس هاتون بکنید .
یا حق .
در اکثر موارد وقتي تصميم ميگيريم که برنامه رو به کلاس هاي مجزا تقسيم کنيم و هر کلاس رو در کتابخانه اي جدا پياده کنيم در بخش هايي نياز ميشه که از کلاسي در کلاس ديگه استفاده بشه ، در اين مواقع بارها شده که با پيغاه خطا Circular unit reference مواجه شديد ، اين پيغام زماني مشاهده ميشه که از دو کتابخانه داخل يکديگر استفاده کرده باشيم ، مثلا :
unit Unit1;
interface
uses Unit2;
implementation
end.
unit Unit1;
interface
uses Unit1;
implementation
end.
يکي از راه حل ها در اين مواقع استفاده از Class Helper ها هست ( البته راه حل هايي مثل جدا کردن ساختارهايي که زياد استفاده ميشن در يک کتابخانه جداگانه يا طراحي مجدد و اصولي کلاسي که نوشته شده قبل از اين راه حل پيشنهاد ميشه ! )
خوب فرض کنيد کتابخانه هايي مثل اين :
unit Dog;
interface
type
TDog = class
Name: String;
end;
implementation
end.
و اين داريم :
unit Master;
interface
type
TMaster = class
Name: String;
end;
implementation
end.
خوب حالا تو برنامه براي استفاده از اين دو برادر عزيز ازشون نمونه سازي ميکنيم :
uses
Dog, Master;
var
Dog: TDog;
Master: TMaster;
begin
Dog := TDog.Create;
Master := TMaster.Create;
try
Dog.Name := 'Bobby';
Master.Name := 'Joe';
finally
Dog.Free;
Master.Free;
end;
end;
حالا تصميم ميگيريم به کلاس TDog متدي با نام Master اضافه کنيم ( از نوع TMaster ) که مشخصات مربي اين سگ رو نگهداري کنه :
unit Dog;
interface
uses Master;
type
TDog = class
Name: String;
Master: TMaster;
end;
implementation
end.
تا اينجا همه چي آرومه ، من چقدر خوشحالم ... :چشمک:
خوب حالا نياز داريم تو کلاس مربي هم يک متد با نام Dog داشته باشيم ( از نوع TDog ) که مشخصات سگ اين مربي رو نگهداري کنه ، خوب کد زير رو مينويسم :
unit Master;
interface
uses
Dog;
type
TMaster = class
Name: String;
Dog: TDog;
end;
implementation
end.
خوب ؟! همونطور که میبینید با پیغام خطا Circular unit reference عملیات کامپایل متوقف میشه .
اولین چیزی که به ذهن میرسه این هست که متد Dog رو از نوع TObject در نظر بگیریم ، یعنی :
unit Master;
interface
type
TMaster = class
Name: String;
Dog: TObject;
end;
implementation
end.
کد زیر بدون مشکل کامپایل میشه ولی بدیش این هست که برای هر کاری باید Typecast های متعددی انجام بدید ، مثلا برای نمایش نام مربی یک سگ :
Writeln(TDog(Master.Dog).Name);
و حالا راه حلی با استفاده از Class Helper ها ؛
اوین قدم تعریف Dog به صورت یک فیلد Protected در کلاس TMaster هست ، یعنی کلاس TMaster رو به صورت زیر مینویسم :
unit Master;
interface
type
TMaster = class
protected
fDog: TObject;
public
Name: String;
end;
implementation
end.
حالا کافیه به وسیله Class Helper ها یک پراپرتی برای کلاس TMaster بنویسیم تا شئ Dog رو به صورت یک نمونه از کلاس TDog به ما تحویل بده :
unit MasterClassHelper;
uses
Dog, Master;
type
TMasterClassHelper = class helper for TMaster
private
procedure SetDog(Value: TDog);
function GetDog: TDog;
public
property Dog: TDog read GetDog write SetDog;
end;
function TMasterClassHelper.GetDog: TDog;
begin
Result := TDog(FDog); // cast the TObject(FDog) to TDog!
end;
procedure TMasterClassHelper.SetDog(Value: TDog);
begin
FDog := Value;
end;
و حالا تو برنامه خیلی راحت بدون هیچ Typecast ی میتونیم از کلاس TMaster و TDog استفاده کنیم :
Writeln(Master.Dog.Name);
Writeln(Dog.Master.Name);
حتی :
Writeln(Dog.Master.Dog.Master.Dog.Master.Dog.Name) ;
:چشمک: آقا جنبه داشته باش !
همچنین با use کردن کتابخانه MasterClassHelper در کتابخانه Master علاوه بر کلاس TMaster میتونید به کلاس TDog هم دسترسی داشته باشید :
Unit unit1;
type
TMaster = class
protected
FDog: TObject;
public
Name: String;
procedure RenameDog;
end;
implementation
uses
MasterClassHelper;
procedure TMaster.RenameDog;
begin
Dog.Name := 'Buddy';
end;
در آخر اگر مجبور به استفاده از این مطلب شدید بهتره یه جفت کتاب در مورد اصول برنامه نویسی شئ گرا بخونید و تجدید نظری در مورد طراحی کلاس هاتون بکنید .
یا حق .