======Record and Object======
----
=====Record=====
คือ Data Type ชนิดหนึ่ง เรียกว่า Structural Type เป็นตัวแปรที่เก็บข้อมูลได้หลายประเภท ซึ่งแตกต่างจาก Array ที่สามารถเก็บข้อมูลได้แค่ประเภทเดียวกัน รูปแบบการประกาศ Record มีดังนี้
Type
TMyRec = record
Name:string;
Value: integer;
end;
จากการประกาศข้างบน เราได้สร้าง Data Type ที่ชื่อว่า TMyRec ขึ้นมา ซึ่งเป็น Record
program Simple_Record;
type
TMyRec = record
Name:string;
Value:integer;
Arr:array[1..3] of real;
end;
var
MyRec1: TMyRec;
begin
MyRec1.Name:='MyRec1';
MyRec1.Value:=1;
MyRec1.Arr[1]:=1.234;
MyRec1.Arr[2]:=5.123;
MyRec1.Arr[3]:=7.890;
writeln('MyRec1.Name = ',MyRec1.Name);
writeln('MyRec1.Value = ',MyRec1.Value);
writeln('MyRec1.Arr[1] = ',MyRec1.Arr[1]);
writeln('MyRec1.Arr[2] = ',MyRec1.Arr[2]);
writeln('MyRec1.Arr[3] = ',MyRec1.Arr[3]);
readln();
end.
Compiled Results:
MyRec1.Name = MyRec1
MyRec1.Value = 1
MyRec1.Arr[1] = 1.2340000000000000E+000
MyRec1.Arr[2] = 5.1230000000000002E+000
MyRec1.Arr[3] = 7.8899999999999997E+000
\\ \\
=====Advanced Record=====
ปกติแล้ว Record จะมีแค่ Field Variable อยู่ภายใน แต่ยังมี Record อีกประเภทหนึ่งที่มี Method และ Property ได้ เรียกว่า Advanced Record \\
Advanced Record ถูกสร้างขึ้นเพื่อให้มีความสามารถทางด้าน Object Oriented Programming \\
การประกาศ Advanced Record จำเป็นต้องมี Compiler Directive ต่อไปนี้
{$modeSwitch advancedRecords}
จากนั้น เราก็สามารถประกาศ Procedure/Function บน Record ได้ดังนี้
Type
TMyRec = record
FName:string;
FValue: integer;
Procedure DoSomething;
Function GetSomething:integer;
end;
program Advanced_Record;
{$modeSwitch advancedRecords}
type
{ TMyRec }
TMyRec = record
Name:string;
Value:integer;
Arr:array[1..3] of real;
procedure ShowData;
function GetValue:integer;
end;
var
MyRec1: TMyRec;
{ TMyRec }
procedure TMyRec.ShowData;
begin
writeln('Name = ',Name);
writeln('Value = ',Value);
writeln('Arr[1] = ',Arr[1]);
writeln('Arr[2] = ',Arr[2]);
writeln('Arr[3] = ',Arr[3]);
end;
function TMyRec.GetValue: integer;
begin
result:=Value*100;
end;
begin
MyRec1.Name:='MyRec1';
MyRec1.Value:=1;
MyRec1.Arr[1]:=1.234;
MyRec1.Arr[2]:=5.123;
MyRec1.Arr[3]:=7.890;
MyRec1.ShowData;
writeln('MyRec.GetValue = ',MyRec1.GetValue);
readln();
end.
Compiled Results:
Name = MyRec1
Value = 1
Arr[1] = 1.2340000000000000E+000
Arr[2] = 5.1230000000000002E+000
Arr[3] = 7.8899999999999997E+000
MyRec.GetValue = 100
\\ \\
----
=====Object=====
ถูกสร้างขึ้นมาตั้งแต่สมัยที่ยังใช้ Turbo Pascal โดยออกแบบให้เป็นไปตามหลักการ Object Oriented Programming (OOP) ลักษณะการใช้งานโดยทั่วไปจะเหมือนกันกับ Advanced Record \\ \\
สาเหตุที่มีให้ใช้ทั้ง Object และ Advanced Record นั้น เพราะว่าภายหลังจากที่ Delphi เปิดตัว ได้มีการนำ Class มาใช้แทน Object และผลักดันให้ เรียก Object ว่าเป็น Advanced Record แทน (อันนี้ผมเข้าใจว่า เพราะคำว่าว่า Object ในภาษาทั่วไป คือ instance ซึ่งแปลว่าผลิตภัณท์ที่เกิดจากต้นแบบ (Class) ซึ่งอาจสร้างความสับสนให้ผู้ใช้ได้) ทีนี้ทางฝั่งของ FPC ซึ่งมีการออกแบบให้โค๊ดทั้งหลายคล้ายคลึงกับทั้งทาง Turbo Pascal และ Delphi อยู่แล้วนั้น ได้ตัดสินใจเก็บ Object ไว้ เพื่อให้ยัง Compatible กับ Turbo Pascal อยู่นั่นเอง แต่ถึงอย่างไรทั้งสอง ก็ถูกใช้งานเหมือนกัน
การประกาศ Object อย่างง่าย ทำได้ดังนี้
Type
TMyRec = object
FName:string;
FValue: integer;
Procedure DoSomething;
Function GetSomething:integer;
end;
อย่างไรก็ตาม ในการนำ Object ใช้งานจริงนั้น ไม่ได้มีเพียงแค่ Field กับ Method ดังที่เห็นในตัวอย่าง แต่ยังมีส่วนประกอบอื่นๆอีกมากมาย อาทิเช่น Property, Access Specifiers เป็นต้น นอกจากนี้ Object ยังสามารถถ่ายทอดความสามารถ (Inheritance) ไปสู่ Object อื่นได้ ซึ่งรายละเอียดเหล่านี้จะขอนำไปกล่าวในเนื้อหาของ Class ต่อไป เพราะ Class กับ Object ต่างมีหลักการคล้ายคลึงกัน\\
ตัวอย่างการสร้าง Object แบบละเอียด
Type
TMyItem = object
private
FName:string;
FValue:integer;
function GetIsOK:boolean;
public
procedure DoSomeThing;
property Name:string read FName write FName;
property Value:integer read FValue write FValue default 0;
property IsOK:boolean read GetIsOK;
end;
\\
จากตัวอย่างโค๊ดข้างบน จะอธิบายส่วนประกอบต่างๆทีละส่วนดังนี้
====Access Modifier====
type
TObj = object
private
{ private declarations }
public
{ public declarations }
end;
คือ private และ public โดยแต่ละตัวกำหนดความสามรถในการเข้าถึง Field, Procedure, Function หรือ Property ที่อยู่ภายใต้ สรุปได้ดังนี้
^ Access Modifier ^ Descriptions ^
|private| มองเห็นได้เฉพาะที่อยู่ใน Unit เดียวกันกับ Object นี้เท่านั้น |
|public| มองเห็นได้จากทุกที่ที่มีการ uses Unit นี้ |
|ไม่ระบุ| หมายถึง Public |
====Field Parameters====
คือ ส่วนที่ถูกประกาศเป็นตัวแปรคล้ายกับการประกาศ var จากตัวอย่างข้างบน คือ FName และ FValue ตัวแปรเหล่านี้มักถูกประกาศเพื่อใช้เก็บค่าสำหรับ Property \\
จากตัวอย่างข้างบน จะเห็นว่า Field ถูกประกาศภายใต้คำว่า Private เพราะเราใช้ Field เหล่านี้ไว้เก็บค่า Property ที่ชื่อว่า Name และ Value จึงไม่ต้องการให้ Field เหล่านี้ถูกแก้ใขได้จากภายนอก
====Properties====
คือ คุณสมบัติของ Object โดยรายละเอียดจะขอกล่าวในหัวข้อ Class อีกที\\ \\
**ตัวอย่าง** การสร้าง Object ทรงกระบอก Cylinder เพื่อรับค่า รัศมีวงกลม (Radius) กับ ความสูง (Height) เข้ามาเป็น Input \\ จากนั้น
จึงนำมาคำนวณพื้นที่ฐาน (Area) และปริมาตร (Volume) \\ \\
__ข้อสังเกต__: ตัวอย่างนี้ แสดงให้เห็นปัญหาของการไม่ใช้ Property จะสังเกตว่า ตอนแรกนั้นเราใส่ค่า Radius = 2 และ Height = 10 แล้วสั่งคำนวณค่าจาก CalculateArea, CalculateVolume จะได้ค่า Area, Volume มาตามปกติ \\ \\ แต่หลังจากนั้น เมื่อเปลี่ยนค่า Radius เป็น 10 แล้วลองไม่เรียก CalculateArea, CalculateVolume เมื่อเรียกดูค่า Area, Volume จะเห็นว่ายังเป็นค่าเดิม เพราะไม่ได้มีการ Update ค่า Area, Volume เหมือนตอนแรก\\ \\ สรุปได้ว่า เมื่อมีการเปลี่ยนค่า Radius หรือ Height จะต้องมีการเรียก CalculateArea, CalculateVolume ทุกครั้ง จึงจะได้ค่า Area, Volume ที่ถูกต้อง ลองจินตนาการถึงการเขียนโปรแกรมจริงๆว่าหากเรามี Input กับ Output ที่มากขึ้นในระดับ 10-20 ตัว ซึ่งแต่ละตัวมีการคำนวณค่าที่ซับซ้อน ที่ผ่าน Procedure/Function หลายตัว คงเป็นการลำบากที่จะต้องมาเรียก Procedure/Function เหล่านั้น ทุกครั้งที่มีการ update ค่า Input นี่จึงเป็นเหตุผลหนึ่ง ที่เราควรใช้งาน Property แทนที่ตัวแปร Input/Output พวกนี้ สำหรับการเปลี่ยนมาใช้ Property ให้ดูได้จาก Example-4
program Cylinder_Without_Properties;
type
{ TMyCylinder }
TMyCylinder = Object
Radius:real; //Input
Height:real; //Input
Area:real; //Output
Volume:real; //Output
procedure CalculateArea;
procedure CalculateVolume;
procedure ShowData;
end;
{ TMyCylinder }
procedure TMyCylinder.CalculateArea;
begin
Area:=system.pi*(Radius*Radius);
end;
procedure TMyCylinder.CalculateVolume;
begin
Volume:=Area*Height;
end;
procedure TMyCylinder.ShowData;
begin
writeln('Radius = ',Radius);
writeln('Area = ',Area);
writeln('Volume = ',Volume);
end;
var
Cylinder:TMyCylinder;
begin
Cylinder.Radius:=2; //Set Radius = 2
Cylinder.Height:=10; //Set Radius = 2
Cylinder.CalculateArea; //Calculate Area of Cylinder
Cylinder.CalculateVolume; //Calculate Volume of Cylinder
Cylinder.ShowData; //Show the results
Cylinder.Radius:=10; //Change Radius to 10
Cylinder.ShowData; //Show the result without calling CalculateArea
readln;
end.
**ตัวอย่าง** การสร้าง Object ทรงกระบอก Cylinder เพื่อรับค่า รัศมีวงกลม (Radius) กับ ความสูง (Height) เข้ามาเป็น Input \\ จากนั้น
จึงนำมาคำนวณพื้นที่ฐาน (Area) และปริมาตร (Volume) \\ \\
__ข้อสังเกต__: ตัวอย่างนี้ ใช้ Property แทนตัวแปร Output และเปลี่ยน CalculateArea, CalculateVolume ไปเป็น Getter แทน \\ \\
จะสังเกตว่า เมื่อเราเปลี่ยนค่า Radius หรือ Height ค่าของ Area, Volume ก็จะถูก Update ตามเสมอ เพราะอย่างเช่นที่เคยอธิบายข้างบนไว้แล้วว่า Getter จะถูกเรียกทุกครั้ง ที่ Property ตัวนั้นถูกเรียกให้แสดงค่า ซึ่งในกรณีนี้ ถูกเรียกให้แสดงค่าใน ShowData นั่นเอง
program Cylinder_With_Properties;
type
{ TMyCylinder }
TMyCylinder = Object
private
function GetRadius: real;
function GetVolume: real;
public
Radius:real; //Input
Height:real; //Input
property Area:real read GetRadius; //Output
property Volume:real read GetVolume; //Output
procedure ShowData;
end;
{ TMyCylinder }
function TMyCylinder.GetRadius: real;
begin
result:=system.pi*(Radius*Radius);
end;
function TMyCylinder.GetVolume: real;
begin
result:=Area*Height;
end;
procedure TMyCylinder.ShowData;
begin
writeln('Radius = ',Radius);
writeln('Area = ',Area);
writeln('Volume = ',Volume);
end;
var
Cylinder:TMyCylinder;
begin
Cylinder.Radius:=2; //Set Radius = 2
Cylinder.Height:=10; //Set Height = 10
Cylinder.ShowData; //Show the results
Cylinder.Radius:=10; //Change Radius to 10
Cylinder.ShowData; //Show the results
Cylinder.Radius:=5; //Change Radius to 5
Cylinder.Height:=2; //Change Height = 2
Cylinder.ShowData; //Show the results
readln;
end.
Compiled Results:
\\