User Tools

Site Tools


Sidebar


Introduction


Basic Tutorials


Advance Tutorials


Useful Techniques


Examples

  • Simple Pipe Weight Calculator
  • Unit Convertor

Sidebar

tutorial:record

This is an old revision of the document!


Record and Object


Record

คือ Data Type ชนิดหนึ่ง เรียกว่า Structural Type เป็นตัวแปรที่เก็บข้อมูลได้หลายประเภท ซึ่งแตกต่างจาก Array ที่สามารถเก็บข้อมูลได้แค่ประเภทเดียวกัน รูปแบบการประกาศ Record มีดังนี้

Type
   TMyRec = record
     Name:string;
     Value: integer;     
   end;

จากการประกาศข้างบน เราได้สร้าง Data Type ที่ชื่อว่า TMyRec ขึ้นมา ซึ่งเป็น Record

Example-1: Record

Example-1: 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;

Example-2: Advanced Record

Example-2: Advanced Record

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 ประกอบไปด้วย Getter (คำสั่งหลัง read) และ Setter (คำสั่งหลัง write)

เพื่อเป็นการอธิบายให้เห็นภาพ จะขอยกตัวอย่าง

property ValueX:integer read GetValueX write SetValueX;

จากตัวอย่างดังกล่าว Getter คือ GetValueX ส่วน Setter คือ SetValueX
Getter จะถูกเรียกใช้งาน เมื่อมีการเรียกใช้ค่าของ Property ดังเช่น

writeln(ValueX);

Setter จะถูกเรียกใช้งาน เมื่อมีการใส่ค่าให้ Property เช่น

ValueX:=12;

ทำไมต้องมี Getter Setter ให้ยุ่งยาก แทนที่จะใช้ var หรือ Field อย่างเดียว? ประโยชน์ของการใช้งาน Property มีดังนี้ครับ

  • ใช้คัดกรองตัวแปร บางครั้งเรารับค่าเพื่อการคำนวณในช่วง 0-100 เท่านั้น หากผู้ใช้ใส่ค่าเกินกว่าค่าดังกล่าวมา จะสามารถคัดกรองให้เป็นค่าที่อยู่ในช่วง 0-100 แทนได้
  • เพื่อให้ค่าของ Property ได้มีการ update ตลอดเวลา โดยเฉพาะหาก property ของเรา ขึ้นอยู่กับค่าอื่น เมื่อใดที่มีการเปลี่ยนค่า ก็จะมีการ update ค่า property เราด้วยเสมอ

รูปแบบการเขียน Property มีหลักๆดังนี้

  • Property ที่มี Getter, Setter เป็น Field
property Value:integer read FValue write FValue;
  • property ที่มี Getter, Setter เป็น Procedure/Function ซึ่งโดยปกติ Getter จะเป็น Function ส่วน Setter จะเป็น Procedure
property ValueX:integer read GetValueX write SetValueX;
function GetValueX:integer;
procedure SetValueX(Value:integer);
  • Property ที่แสดงค่าได้อย่างเดียว (ReadOnly)
property IsOK:boolean read GetIsOK;

Example-3: Object without Properties

Example-3: Object without Properties

ตัวอย่าง การสร้าง 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.   

Example-4: Object Properties

Example-4: Object Properties

ตัวอย่าง การสร้าง 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:


tutorial/record.1548645183.txt.gz · Last modified: 2019/01/28 10:13 by admin