الفصل السادس
ال Object Oriented Programming موضوع لذيذ جدا وهو مش فيه اى جديد عن حياتك.. اذا بصيت لأى شئ حواليك هتلقيه Object انسان عربية طيارة شجرة عمارة.. الخ
كلهم بيشتركو فى كلمة انهم Object! ولكن لكل شئ فيهم خصائص محددة ووظائف خاصة فيه نيجى الأول نفرق ال Class عن ال Object
على فرض لدينا لوحة مرسوم عليها تصميم عمارة => هى دى الصف
لكن اذا تم انشاء العمارة من التصميم دا => هو دا الكائن
عندك تصميم لروبوت ومتحدد عليه خصائصه ووظايفه => هو دا الصف
اذا شركة اخدت التصميم دا ونفذته => هو دا الكائن
على فرض اننا بنعمل تصميم لإنسان (على الورق) يعنى الصف
type THuman=class(TObject)
Constructor Create(aName: String; aSex: Char; aColor: String);
private
fname: String;
fsex: Char;
fcolor: String;
public
Function GetSex(): Char;
Procedure SetSex(aSex: Char);
Function GetColor(): String;
Procedure SetColor(aColor: String);
Function GetName(): String;
Procedure SetName(aName:String);
Procedure Eat();
Procedure Sleep();
end;
اول كلمة هتقلها ماهذا كله ؟
نمشى مع الكود واحدة واحدة
type Thuman=class(TObject)
معناها أننا بنعرف Type جديد بإسم Thuman (لاحظ ان اى Type) بنبدأه ب T “مجرد عادة”
الإنسان دا ليه مميزات او صفات مثل ان ليه اسم وليه لون وليه نوع ودى اسمها fields
(لاحظ ان اى field بنبدأه ب f)
لن نسمح لأى حد بلمسها -يعدل فيها او يتعامل معاها- غيرى انا -الصف- وبموافقتى وشروطى ودا بيتم من خلال ال Getters/Setters methods لو لاحظت ان كل Field ليه Getter/Setter
fname ليها
Function GetName(): String;
Procedure SetName(aName:String);
fsex ليها
Function GetSex(): Char;
Procedure SetSex(aSex: Char);
fcolor ليها
Function GetColor(): String;
Procedure SetColor(aColor: String);
ال Get/Set Methods بنبدأها بشئ مناسب Get/Set
نيجى بعد كدا للوظائف الذى بيعملها ال Human دا وهى مثلا Eat, Sleep وwalk و Work وغيرهم دى اسمها methods
لاحظ اى Function/Procedure داخل صف بيسمى Method او تحديدا Instance Methods إلا فى حال اننا حددناها انها Class Method (مش تصعبها على نفسك حاليا)
جميل جدا كدا نحن بنخلى العالم الخارجى يتعامل مع ال Fields الخاصة بالصف عن طريق ال Getters/Setters ولكن اذا لاحظت انها غلسة شوية.. ناس كتير اشتغلت Vbاو Delphi وكان عندهم مفهوم كلمة ال Properties وهنا الموضوع بيكون الذ شوية كالتالى
type THuman=class(TObject)
Constructor Create(aName: String; aSex: Char; aColor: String);
private
fname: String;
fsex: Char;
fcolor: String;
Function GetSex(): Char;
Procedure SetSex(aSex: Char);
Function GetColor(): String;
Procedure SetColor(aColor: String);
Function GetName(): String;
Procedure SetName(aName:String);
Procedure Eat();
Procedure Sleep();
public
property Name: String read GetName write SetName;
property Sex: Char read GetSex write SetSex;
property Color: String read GetColor write SetColor;
Procedure InspectMe();
end;
لاحظ اننا خلينا ال Getters/Setters فى ال Private Section :S
طب العالم الخارجى هيتعامل ازاى مع ال Fields الخاصة بنا كدا ؟ بكل بساطة هيستخدم ال Properties مثل Name, Sex, Color
لعمل اى Property بتستخدم الصيغة العامة كالتالى
property property_name : Type read Getter write Setter;
كالتالى مثلا
property Name: String read GetName write SetName
;
هنا لدينا Property -خاصية- بإسم Name وهى من النوع String (بتتعامل بال String) سواء فى عملية ال Get او ال Set
.Name=val //set(write), sets fname to a string
.Name //Get(read), returns fname (a string)
جميل سامع واحد بيقول انت بتشتغلنا ياعم! فين الكود نفسه؟
هقله اتقل شوية نحن لحد الآن فى ال Interface (مبرمجى السى/++ بيحبو يسموه ال Header) لسه هنعمل ال Implementation فى قسم لوحدها كالتالى
Constructor THuman.Create(aName: String; aSex: Char; aColor: String);
begin
//inherited;
fname:=aName;
fsex:=aSex;
fcolor:=aColor;
end;
Function THuman.GetName(): String;
begin
Result := Self.fname;
end;
Function THuman.GetSex(): Char;
begin
Result := Self.fsex;
end;
Procedure THuman.SetName(aName: String);
begin
Self.fname:=aName;
end;
Procedure THuman.SetSex(aSex: Char);
begin
Self.fsex:=aSex;
end;
Function THuman.GetColor(): String;
begin
Result:= Self.fcolor;
end;
Procedure THuman.SetColor(aColor: String) ;
begin
Self.fcolor:=aColor;
end;
Procedure THuman.InspectMe() ;
begin
WriteLn('Name: ', Self.fname);
WriteLn('Color: ', Self.fcolor);
WriteLn('Sex: ', Self.fsex);
end;
Procedure THuman.Eat()
begin
//Code to eat.
end;
Procedure THuman.Sleep()
begin
//Code to sleep.
end;
اوبس فى واحد بيقولى انت نسيت تقول ال Constructor دا يعنى إيه!
ال Constructor بكل بساطة هو Function ولكن ليها اسم مميز Constructor .. لماذا ؟
جميل نحن قلنا ان اى كائن بيمر على مرحلتين
المرحلة الأولى التصميم والتانى الإنشاء
التصميم هو الذى نجهز فيه بقالنا فترة وتحديد ال Fields و ال Methods وال Properties وغيرهم لكن الإنشاء هو اننا نحول التصميم دا لكائن ودا بيتم عن طريق ال Constructor “المنشئ او المشيد” وفيه بيتجهز ال Data Members (مسمى تانى لل Fields) بالقيم الإفتراضية مثلا الString ياخد Null والInt ياخد Zero وهكذا * او * القيم الذى انت تريدها (ال Arguments الذى هتتمرر لل Constructor ) فعلا انت راجل 10/10
مثل ماحصل هنا بالظبط
Constructor THuman.Create(aName: String; aSex: Char; aColor: String);
begin
//inherited;
fname:=aName;
fsex:=aSex;
fcolor:=aColor;
end;
ملحوظة: مش لازم تسمى ال Constructor بإسم Create ولكنه تقليد متعارف عليه ^_^
جميل جدا الصف الجميل الذى كتبناه دا مش هنسيبه متعلق فى الهوا كدا لازم نحطه فى مكان مناسب ليه
اعمل وحدة جديدة
File -> New Unit
وسميها وليكن Creatures وضيف فيها الذى كتبناه كالتالى
ملحوظة: ال UNIT -الوحدة- هى ملف بنجمع فيه الأجزاء المترابطة من شغلنا مثلا لو بندرس ال OOP وبنعمل صفات للدراسة مثل Human, Robot, Bird, .. etc نخليها Creatures او مثلا Employer, Employee, Customer, .. etc تخليها Business وهكذا…
unit creatures;
{$mode objfpc}{$H+}
interface
type THuman=class(TObject)
Constructor Create(aName: String; aSex: Char; aColor: String);
private
fname: String;
fsex: Char;
fcolor: String;
Function GetSex(): Char;
Procedure SetSex(aSex: Char);
Function GetColor(): String;
Procedure SetColor(aColor: String);
Function GetName(): String;
Procedure SetName(aName:String);
Procedure Eat();
Procedure Sleep();
public
property Name: String read GetName write SetName;
property Sex: Char read GetSex write SetSex;
property Color: String read GetColor write SetColor;
Procedure InspectMe();
end;
implementation
Constructor THuman.Create(aName: String; aSex: Char; aColor: String);
begin
//inherited;
fname:=aName;
fsex:=aSex;
fcolor:=aColor;
end;
Function THuman.GetName(): String;
begin
Result := Self.fname;
end;
Function THuman.GetSex(): Char;
begin
Result := Self.fsex;
end;
Procedure THuman.SetName(aName: String);
begin
Self.fname:=aName;
end;
Procedure THuman.SetSex(aSex: Char);
begin
Self.fsex:=aSex;
end;
Function THuman.GetColor(): String;
begin
Result:= Self.fcolor;
end;
Procedure THuman.SetColor(aColor: String) ;
begin
Self.fcolor:=aColor;
end;
Procedure THuman.InspectMe() ;
begin
WriteLn('Name: ', Self.fname);
WriteLn('Color: ', Self.fcolor);
WriteLn('Sex: ', Self.fsex);
end;
Procedure THuman.Eat()
begin
//Code to eat.
end;
Procedure THuman.Sleep()
begin
//Code to sleep.
end;
end.
واعمل save ليها
وتعالى نختبرها فى اى برنامج مثلا oopconcepts1
كالتالى مثلا
program ooConcepts1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes, creatures
{ you can add units after this };
var
ahmed: THuman ;
begin
ahmed:=THuman.Create('Ahmed', 'm', 'white');
ahmed.InspectMe();
ahmed.Name:='youssef';
ahmed.InspectMe();
ReadLn;
end.
الوراثة
اعمل برنامج جديد وضيف ليه unit بإسم creatures
type
{ THuman }
THuman=class(TObject)
Constructor Create(aName: String; aSex: Char; aColor: String);
private
fname: String;
fsex: Char;
fcolor: String;
function GetColor: String;
function GetName: String;
Function GetSex(): Char;
Procedure SetSex(aSex: Char);
Procedure SetColor(aColor: String);
Procedure SetName(aName:String);
public
class Procedure About; virtual;
property Name: String read GetName write SetName;
property Sex: Char read GetSex write SetSex;
property Color: String read GetColor write SetColor;
Procedure Eat();
Procedure Sleep();
Function InspectMe(): String; virtual; //Should be overriden in the child.
end;
صف جديد بإسم THuman
فى هنا fields لل name, sex, color وفى private getters/setters
وفى public functions/procedures ومجموعة من ال properties
about: يعرض كلمة عن الصف دا والمفترض انه يتغير فى ال child
الprototype ليه كالتالى
class Procedure About; virtual;
هنا لاحظ اننا بادئين التعريف ب class لنعبر على ان الطريقة دى خاصة بالصف يعنى تستدعيها ب
Thuman.About
بدون الشئ لإنشاء object من ال Thuman class
فى Function بإسم InspectMe للحصول معلومات عن ال fields الموجودة بالصف ولكن المفروض نعيد تعريفها فى اى صف هيورثه
نيجى لل implementation الخاصة ب الصف دا
{ THuman }
constructor THuman.Create(aName: String; aSex: Char; aColor: String);
begin
Self.fname:=aName;
Self.fsex:=aSex;
Self.fcolor:=aColor;
end;
function THuman.GetColor: String;
begin
Result:=Self.fcolor;
end;
function THuman.GetName: String;
begin
Result:=self.fname;
end;
function THuman.GetSex(): Char;
begin
Result:=self.fsex;
end;
procedure THuman.SetSex(aSex: Char);
begin
self.fsex:=aSex;
end;
procedure THuman.SetColor(aColor: String);
begin
self.fcolor:=aColor;
end;
procedure THuman.SetName(aName: String);
begin
self.fname:=aName;
end;
procedure THuman.Eat();
begin
WriteLn('Eatin');
end;
procedure THuman.Sleep();
begin
WriteLn('Sleepin');
end;
class procedure THuman.About;
begin
WriteLn('Human class');
end;
function THuman.InspectMe(): String;
begin
Result:='Name: '+self.fname + LineEnding + 'Sex: '+self.fsex + LineEnding+ 'Color: '+self.fcolor;
end;
واضحة مثل المثال السابق ولكن ال Thman.InspectMe محتاجة بعض التوضيح
function THuman.InspectMe(): String;
begin
Result:='Name: '+self.fname + LineEnding + 'Sex: '+self.fsex + LineEnding+ 'Color: '+self.fcolor;
end;
هى بتعمل دمج لمجموعة من ال strings + بتضيف LineEnding (لو متأقلم مع ال Escape Sequences فهى ال n)
نيجى بقة لل TEmployer هو صف بيعبر عن انسان ليه اسم ولون ونوع + (مرتب)
فياإما نكتب كل الصف THuman داخل ال Temployer او نستخدم ال وراثة بإننا نورث ال Thuman لل Temployer ونضيف الجديد ليه
type
{ TEmployer }
TEmployer=class(THuman)
Constructor Create(aName: String; aSex: Char; aColor: String; aSalary:Integer);
private
fsalary:Integer;
Function GetSalary():Integer;
Procedure SetSalary(aSalary:Integer);
public
class Procedure About; override;
Function InspectMe(): String;override;
property Salary:Integer read GetSalary write SetSalary;
end;
بمجرد انك كتبت
Temployer=class(THuman)
فهنا ال Temployer هيورث كل الذى فيه ال Thuman
ال
Function InspectMe(): String;override;
لازم نعيد تعريفها بحيث تتوافق مع الخصائص الجديدة للصف دا مثل المرتب مثلا!
لاحظ اننا سنعيد تعريف ال About, InspectMe
الآن ال Implementation الخاصة به
{ TEmployer }
constructor TEmployer.Create(aName: String; aSex: Char; aColor: String;
aSalary: Integer);
begin
inherited Create(aName, aSex, aColor);
self.fsalary:=aSalary;
end;
اولا ال Constructor
هنا الأول لازم نمرر ال قيم الخاصة بالصف الأب الذى هى ال aName, aSex, aColor للمشيد الخاص بالTHuman وال fields الجديدة نجهزها مثل ال fsalary
لاحظ استخدام inherited هنا دى بتنقلك للالأب صف الخاص بال Temployer وبعدها Create “بتعبر عن ال Thuman.Create “
function TEmployer.GetSalary(): Integer;
begin
Result := self.fsalary;
end;
procedure TEmployer.SetSalary(aSalary: Integer);
begin
self.fsalary:=aSalary;
end;
class procedure TEmployer.About;
begin
WriteLn('Employer class!');
end;
هنا اعادة تعريف ل About
function TEmployer.InspectMe(): String;
begin
Result := inherited InspectMe + LineEnding + 'Salary: '+IntToStr(self.fsalary);
end;
هنا اعادة تعريف ل InspectMe بنستدعى ال InspectMe الخاصة ب الأب الذى هترجعلنا String
function THuman.InspectMe(): String;
begin
Result:='Name: '+self.fname + LineEnding + 'Sex: '+self.fsex + LineEnding+ 'Color: '+self.fcolor;
end;
وبعدها ندمج الناتج ب LineEnding و جملة
'Salary:' +IntToStr(self.fsalary)
هنا بنحول قيمة ال fsalary ل string لأنها integer ومش ينفع ندمج string مع integer
الآن البرنامج
program ooConcepts1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
sysutils, Classes, creatures
{ you can add units after this };
var
ahmed: TEmployer;
begin
ahmed:=TEmployer.Create('Ahmed', 'm', 'white', 90000);
WriteLn(ahmed.InspectMe());
//Set name
ahmed.Name:='youssef';
//Set salary
ahmed.Salary:=100000;
WriteLn(ahmed.InspectMe());
TEmployer.About;
ahmed.Eat;
ReadLn;
end.
انشئ كائن من ال Temployer
ahmed:=TEmployer.Create('Ahmed', 'm', 'white', 90000);
اعرض بيناته
WriteLn(ahmed.InspectMe());
تقدر تعدل فى ال -خصائص- Properties براحتك
اخيرا استخدام ال class method
Temployer.About;
وهى طريقة بتنتمى للصف لا الكائن