إعادة الاستدعاء Callback
#1
السلام عليكم ورحمة الله

Callback بشكل عام هي إعادة توجيه تعليمات تم تمريرها من خلال المعالم (Parameters) إلى تعليمات أخرى
طالع:
كود :
http://en.wikipedia.org/wiki/Callback_(computer_programming)

ومن أشهر الأمثلة على ذلك دوال إعادة الاستدعاء Callback Functions
وهو ما سأقتصر عليه هنا، لذلك قد يبدو العنوان أوسع من الموضوع...

هذا النوع من الاستدعاء له استخدامات كثيرة
توابع Windows API تستخدم مناهج Callback بإفراط
أحيانا لإعادة بيانات وأحيانا أخرى للأحداث
يمكن غالبا محاكاتها بطرق أخرى

ما الفائدة من استخدامها؟
- مرونة أكثر للكود، إعادة الاستخدام وتسهيل الصيانة
- أيضا استخدام شائع الاستدعاء التكراري (أو العودية) Recursivity

لتبسيط المسألة:
لديك تابع يستقبل كمعلمات نص string وعدد صحيح Integer
كود :
procedure MyProcedure(S: string; I: Integer);
هل شعرت يوما أنك ترغب في تمرير إجراء ضمن المعلمات؟
PHP كود :
procedure MyProcedure(S: string; I: Integer; AProcedure: TMyProcedure); 
هنا ستستخدم Callback procedure

لن نقوم باستدعاء دوال API في هذا المثال
بل سنحاول كتابة أبسط مثال Callback من صنعنا كالآتي:
1- أنشئ نمط للإجراء الذي نود تمريره:
PHP كود :
type
  TMyProc 
= procedure(S: string); 
في العبارة السابقة أنشأنا نمط أسميناه TMyProc
في لغات البرمجة كتابة منهج (دالة أو إجراء) بالشكل:
كود :
procedure(S: string)
تدعى بالإمضاء Signature (أو رأس المنهج Header أحيانا):
كود :
http://en.wikipedia.org/wiki/Type_signature
2- نقوم بإنشاء بعض الإجراءات التي سنمررها كأنها متغيرات:
PHP كود :
procedure Proc1(S: string);
begin
  ShowMessage
(S+'1');
end;

procedure Proc2(S: string);
begin
  ShowMessage
(S+'2');
end; 
3- هذا الإجراء لا يستقبل متغيرات string أو Integer بل سيستقبل نمطنا الجديد TMyProc
PHP كود :
procedure CallMyProc(MyProc: TMyProc);
begin
  MyProc
('Call: Proc');
end; 
4- الآن لنقم باستدعاء الإجراء السابق من خلال زر نكتب عند النقر عليه:
PHP كود :
procedure TForm1.Button1Click(Sender: TObject);
begin
  CallMyProc
(Proc1);
  
CallMyProc(Proc2);
end; 
هذا مثال بسيط لتوضيح مبدأ العمل، لذلك فهو لا يوضح الفائدة من الفكرة

* تخيل لو كان الإجراء CallMyProc يقوم بتعليمات كثيرة لا علاقة لها بما خارج الإجراء
وأننا بحاجة إلى استدعاء أحد الإجراءات (كل مرة حسب الحالة) لها نفس الإمضاء
هل سنقوم بنسخ ولصق الكود في كل مرة؟
* حالة أخرى: لدينا دالة تقوم بتعليمات تستغرق وقتا
يرغب من يستدعيها أن يعرف أين وصلت بها الحال (تقدم العملية، بـ TProgressBar مثلا)
في كلا الحالتين يمكننا استخدام CallBack Function

مثال مشابه في المرفق لا يفيد في شيء هو الآخر
غير أنه يستخدم Function لإعادة إشعار تقدم العملية، والمؤشرات لمرونة أكثر Smile

مثال Recursive عند استعراض الملفات:
كود :
http://www.delphi-central.com/callback.aspx
مثال آخر API
كود :
http://delphi.about.com/od/windowsshellapi/a/callback_delphi.htm

بالتوفيق.


الملفات المرفقة
.7z   CallBackSample.7z (الحجم : 1.23 ك ب / التحميلات : 32)
اللهم احقن دماء المسلمين، لا تنسوهم بالدعاء...
[-] كل من 1 user says قال شكرا ل kachwahed على المشاركة المفيدة
  • invocker
الرد
#2
و عليكم السلام و رحمة الله

العملية شبية نوعا ما ب هاكذا اجراءت : Smile
PHP كود :
function add(a, b: Integer): Integer; stdcall;
begin
  Result
:= a + b;
end;

function 
D4(p: Pointer; a,b: Integer): Integer; stdcall
asm
  push b
  push a
  call p
end
;

procedure TForm1.FormCreate(Sender: TObject);
var
  
i: Integer;
begin
  i 
:= D4(@Add, 5, 15);
  
ShowMessage(IntToStr(i));
end; 
الرد
#3
فعلا أخي hanipino أو توجيه pascal، ومباشرة Smile
PHP كود :
ShowMessage(IntToStr(D4(@Add, 5, 15))); 
نسيت أن أنبه على الفرق بين:
PHP كود :
type
  TMyProc 
= procedure(S: string);//(1)
  
TMyObjProc = procedure(S: string) of object;//(2) 
الأول هو نمط لإجراء مستقل يحمل نص string كمعلمة
الثاني هو نمط لإجراء تابع لغرض يحمل نص string كمعلمة
وبالتالي:
PHP كود :
procedure MyProc(S: string);
begin
  
//TMyProc
end;

procedure TForm1.MyObjProc(S: string);
begin
  
//TMyObjProc...
end; 
يمكن استخدام الإجراء الأول MyProc بصفته TMyProc
ويمكن استخدام الإجراء الثاني MyObjProc بصفته TMyObjProc لأنه متعلق بغرض (TForm1 في مثالنا)
ولا يمكن العكس، فهما يمثلان إمضاءين مختلفين.
اللهم احقن دماء المسلمين، لا تنسوهم بالدعاء...
الرد
#4
مثال آخر: أفضل أحيانا من الفحص في كل مرة، أو case..of خاصة من وجود الكثير من الحالات
PHP كود :
var
IniFIle: TMemIniFile;

procedure TfMain.ReadWriteIniFile(Proc: TThreadMethod);
begin
  IniFile 
:= TMemIniFile.Create('.\Data.ini');
  try
    
Proc();
  finally
    
IniFile.UpdateFile;//Important
    
IniFile.Free;
  
end;
end;

procedure TfMain.Save();
begin
  IniFile
.WriteString(Application.Title, Name, edSave.Text);
end;

procedure TfMain.Load();
begin
  edLoad
.Text := IniFIle.ReadString(Application.Title, Name, '<nothing>');
end;

procedure TfMain.btnSaveClick(Sender: TObject);
begin
  ReadWriteIniFile
(Save);
end;

procedure TfMain.btnLoadClick(Sender: TObject);
begin
  ReadWriteIniFile
(Load);
end; 
TThreadMethod = procedure of object معرفة في Classes
و TProcedure = procedure في SysUtils لا داعي لإعادة تعريفها إلا عند القلق بشأن الحجم

بالمناسبة TMemIniFile مستقلة عن API بخلاف TIniFile.
اللهم احقن دماء المسلمين، لا تنسوهم بالدعاء...
الرد


التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم