พูดคุยเกี่ยวกับคลาสและวัตถุใน Delphi
1. ไม่สามารถเข้าใจแนวคิดหลายๆ อย่างโดยไม่ได้รับการศึกษา
เมื่อพูดถึงคลาสและอ็อบเจ็กต์ เราไม่สามารถพูดถึงแนวคิดต่อไปนี้ได้: คลาส วัตถุ และอินสแตนซ์ ส่วนตัวผมคิดว่าไม่เป็นไร
ทำความเข้าใจด้วยวิธีนี้: วัตถุหมายถึงคำทั่วไป และเอนทิตีใดๆ ในธรรมชาติสามารถถือเป็นวัตถุได้ ในขณะที่คลาสหมายถึง
ชุดหมวดหมู่ที่แบ่งออกเป็นลักษณะเฉพาะของออบเจ็กต์เหล่านี้ อินสแตนซ์อ้างอิงถึงออบเจ็กต์ที่อยู่ในหมวดหมู่ใดประเภทหนึ่งโดยเฉพาะ
โอเค ฉันไม่จำเป็นต้องพูดเพิ่มเติมเกี่ยวกับหลักการสำคัญเหล่านี้ ทำไมไม่ลองใช้แนวทางที่ "ตรงกันข้าม" มาใช้ Delphi กันดีกว่า
รหัสเพื่ออธิบายแนวคิดบางอย่างที่ชาวต่างชาติเหล่านี้เสนอมาซึ่งยากสำหรับเราชาวจีนที่จะเข้าใจ:
var
ABtn:Tปุ่ม;
กำหนด ABtn ให้เป็นวัตถุที่อยู่ในคลาส TButton แต่ ABtn ไม่สามารถพูดได้ว่าเป็นอินสแตนซ์เนื่องจากไม่มี
ถูกสร้างขึ้นดังนั้นเราจึงบอกว่าวัตถุถูกกำหนดไว้ไม่มากก็น้อย
บางอย่างก็ไม่แม่นยำพอ -
เริ่ม
ABtn:=TButton.Create(Self);//สร้างอินสแตนซ์ของ TButton
ABtn.Caption:='วัตถุ';
ABtn.ฟรี;
จบ;
2. วัตถุเป็นตัวชี้เข้าออก
จากมุมมองทางกายภาพ วัตถุคือพื้นที่ที่อยู่ และสัญลักษณ์ของพื้นที่ที่อยู่นี้คือสิ่งที่เรากำหนด
คลาส "ตัวแปร" ดังนั้นเราจึงสามารถคิดว่าวัตถุเป็นตัวชี้ไปยังชั้นเรียนได้ อย่างที่เราทุกคนรู้กันดีว่าในการเข้าถึงพอยน์เตอร์ก็แค่
ตัวชี้จะต้องเริ่มต้น เนื่องจากวัตถุเป็นตัวชี้ จึงต้องเตรียมใช้งาน จะเริ่มต้นได้อย่างไร?
เรามาพูดถึงการเริ่มต้นของพอยน์เตอร์กันดีกว่า มีสองวิธีในการเริ่มต้นตัวชี้:
(1) การกระจายโดยตรง
var
ไพน์:^จำนวนเต็ม;
เริ่ม
ใหม่(ไพน์);
ไพน์^:=12;
ทิ้ง(ไพน์);
จบ;
(2) ตัวแปรที่ชี้ไปยังช่องว่างอื่นที่ได้รับการจัดสรร
var
ไพน์:^จำนวนเต็ม;
ฉัน:จำนวนเต็ม;
เริ่ม
ฉัน:=12;
ไพน์:=@i;
จบ;
สิ่งที่น่าสนใจคือมีสองวิธีในการเริ่มต้น "ตัวชี้" เช่นวัตถุ:
(1) การกระจายโดยตรง
var
แบบฟอร์ม:Tแบบฟอร์ม;
เริ่ม
AForm:=TForm.Create(ตนเอง);
AForm.ShowModal;
AForm.ฟรี;
จบ;
(2) ชี้ไปที่กรณีอื่นๆ ของพื้นที่ที่จัดสรรไว้
var
แบบฟอร์ม:Tแบบฟอร์ม;
เริ่ม
AForm:=ตัวเอง;
AForm.Caption:='คุณรู้หรือไม่? ทำไมสิ่งนี้ถึงเกิดขึ้น';
จบ;
file://AForm นี้และอินสแตนซ์ของแบบฟอร์มที่ชี้เพื่อใช้หน่วยที่อยู่เดียวกันและการดำเนินการทั้งหมดใน AForm จะตอบสนอง
file:// ไปยังอินสแตนซ์ของแบบฟอร์มที่เกี่ยวข้อง
เมื่อพูดถึงเรื่องนี้ เราสามารถอธิบายได้อย่างง่ายดายว่าทำไมพารามิเตอร์อ็อบเจ็กต์ของโพรซีเดอร์ (ฟังก์ชัน) จึงถูกส่งผ่านในรูปแบบดังนี้:
(1) PROcedure SetEdit (แก้ไข var: TEdit);
เริ่ม
แก้ไขข้อความ:='11';
จบ;
และ
(2) ขั้นตอน SetEdit (แก้ไข: TEdit);
เริ่ม
แก้ไขข้อความ:='11';
จบ;
ผลก็เหมือนกัน (1) คือการส่งผ่านเอนทิตี TEdit เป็นการอ้างอิงพารามิเตอร์ (2) คือ
ส่งผ่านวัตถุ TEdit "ตัวชี้" เป็นพารามิเตอร์
3. คลาสสามารถเข้าใจได้ว่าเป็นชนิดข้อมูลพิเศษ
เรารู้ว่าประเภทข้อมูลสามารถบังคับให้พิมพ์การแปลงได้ เนื่องจากคลาสสามารถเข้าใจได้ว่าเป็นประเภทข้อมูล
จากนั้นควรจะสามารถดำเนินการแปลงประเภทคลาสได้ ตัวอย่างเช่น รหัสต่อไปนี้เป็นเหตุการณ์การคลิกปุ่ม (Button1):
(หนึ่ง)
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
var
คำบรรยายภาพ:สตริง;
เริ่ม
ACaption:=TButton(Sender).Caption;//Sender แปลงจาก TObject เป็น TButton
ShowMessage(Format('คุณคลิก ''%s'' !',[ACaption]));
จบ;
ในโค้ดนี้ Sender เป็นวัตถุประเภท TObject และเราแปลงเป็นประเภท TButton เหมือนคุณ
หากคุณมองเห็นได้ไม่ชัดเจน คุณสามารถดูการแปลงประเภทข้อมูลตามปกติของเราได้:
(สอง)
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
var
S_Str:สตริง;
P_Str:พีชาร์;
เริ่ม
S_Str:='ฉันรักประเทศจีน!';
P_Str:=PChar(S_Str);
S_Str:='';
S_Str:=สตริง(P_Str);
แสดงข้อความ(S_Str);
จบ;
อย่างไรก็ตาม ในกระบวนการของการเขียนโปรแกรมเชิงวัตถุ เน้นที่ความปลอดภัย ตัวอย่างเช่น การแปลงชนิดบังคับใน (1) มีปัญหามากมาย
ความปลอดภัย. รหัสต่อไปนี้ยังคงเขียนเหตุการณ์ Button1.OnClick:
(สาม)
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
เริ่ม
TCanvas(Sender).Brush.Color:=clRed;
จบ;
หากคุณดำเนินการจะเกิดข้อผิดพลาด สิ่งนี้ไม่ละเมิดวัตถุประสงค์ของการเขียนโปรแกรมเชิงวัตถุใช่หรือไม่ ไม่แน่นอน
ถ้าเป็นคลาสก็ควรมีวิธีการบังคับคลาสเฉพาะคลาส โดยวิธีการเปลี่ยน (3) มีดังนี้
(สี่)
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
เริ่ม
(ผู้ส่งเป็น TCanvas).Brush.Color:=clRed;
end;//ใช้เป็นการแปลงเนื่องจากสามารถตรวจจับข้อผิดพลาดได้และจะไม่ส่งผลต่อการทำงานปกติของโปรแกรม
พูดถึงเรื่องนี้แล้ว ให้ฉันพูดถึง VB ซะก่อน หากคุณได้เรียนรู้ VB แล้ว คุณอาจพบว่าชุดควบคุมในนั้นสนุกกว่า โดยเฉพาะใน
เมื่อเขียนโปรแกรมเหมือนเครื่องคิดเลข แต่เดลฟีให้อะไรเราบ้าง? คำตอบก็คือ Delphi สามารถเปิดได้อย่างรวดเร็วและรัดกุมเช่นกัน
ออกโปรแกรมดังกล่าว หากคุณทำเช่นนี้: ใส่แก้ไขและปุ่มสิบปุ่มบนแบบฟอร์ม ให้แบ่ง Button.Caption ออกเป็น
อย่าตั้งค่าเป็น '0', '1', '2',...'9' จากนั้นเขียนเหตุการณ์ OnClick ของปุ่มดังนี้:
(ห้า)
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
เริ่ม
Edit1.Text:=Edit1.Text+(Sender as TButton).Caption;
จบ;
เชื่อมโยงเหตุการณ์ OnClick ของปุ่มอื่น ๆ กับ Button1Click และเรียกใช้โปรแกรม ปรบมือของคุณ!
ขณะนี้ต้นแบบของโปรแกรมพร้อมใช้งานแล้ว เราใช้การแปลงประเภทคลาสของ Delphi เพื่อพัฒนาฟังก์ชันอาเรย์ควบคุมที่คล้ายคลึงกับใน VB
โปรแกรมก็เยี่ยมเช่นกัน! :)
4. คลาสนามธรรมและอินสแตนซ์
มีคลาสใน Delphi ที่เรียกว่าคลาสนามธรรม และคุณไม่สามารถสร้างอินสแตนซ์ของมันโดยตรงอย่างไร้เดียงสาได้ เช่น: TStrings
ใจดี. รหัสต่อไปนี้:
(หนึ่ง)
var
StrLst:TStrings;
เริ่ม
StrLst:=TStrings.Create;
StrLst.Add('ฉันรักญี่ปุ่น!');
StrLst.ฟรี;
จบ;
นี่ไม่ถูกต้อง แล้วคุณจะสร้างอินสแตนซ์ของคลาสนามธรรมเช่น TStrings ได้อย่างไร คำตอบคือใช้แบบไม่สูบน้ำ
คลาสย่อยของช้าง เรารู้ว่า TStrings มีคลาสย่อยที่ไม่ใช่นามธรรมของ TStringList เราสามารถทำได้:
(สอง)
var
StrLst:TStrings;
เริ่ม
StrLst:=TStringList.Create;//Subclass StrLst ด้วยความช่วยเหลือของตัวสร้างคลาสย่อย
StrLst.Add('ฉันรักประเทศจีน!');
StrLst.ฟรี;
จบ;
(สาม)
var
StrLst:TStringList;
เริ่ม
StrLst:=TStringList.Create;
file://ยอมแพ้ เลิกใช้คลาสนามธรรมอีกต่อไป แค่ใช้ "ลูก" ของมันทำธุรกิจแทน
StrLst.Add('ฉันรักประเทศจีน!');
StrLst.ฟรี;
จบ;
5. คลาสเป็นกลไกที่ห่อหุ้มข้อมูลและการดำเนินงานอย่างมาก
(1) การห่อหุ้มข้อมูล
หน่วย หน่วยที่ 2;
อินเตอร์เฟซ
พิมพ์
พนักงาน=คลาส
ส่วนตัว
FName:สตริง;
สาธารณะ
ตัวสร้างสร้าง;
ฟังก์ชัน GetName:String;
ขั้นตอน SetName (AName:String);
จบ;
การดำเนินการ
{ พนักงาน }
ตัวสร้าง TEmployee.Create;
เริ่ม
FName:='ไฟลุกโชน';
จบ;
ฟังก์ชั่น TEmployee.GetName: สตริง;
เริ่ม
ผลลัพธ์:=FName;
จบ;
ขั้นตอน TEmployee.SetName (AName: String);
เริ่ม
FName:=AName;
จบ;
จบ.
ดังที่แสดงในโค้ดด้านบน เราใช้ขั้นตอน SetName และฟังก์ชัน GetName เพื่อตั้งค่าตัวแปรส่วนตัว FName ให้สมบูรณ์
การห่อหุ้ม นี่คือทั้งหมดที่เราต้องทำกับ FName:
การใช้งาน
หน่วยที่ 2;
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
var
พนักงาน:TEmployee;
เริ่ม
AEmployee:=TEmployee.Create;
AEmployee.SetName('Rose');//ใช้ SetName เพื่อตั้งค่า FName
MessageBox(จัดการ,PChar(AEmployee.GetName),'พนักงาน',0);
file://Use GetName เพื่อเข้าถึง FName
AEmployee.ฟรี;
จบ;
(2) การห่อหุ้มการดำเนินการ
หน่วย หน่วยที่ 2;
อินเตอร์เฟซ
พิมพ์
TDivision=คลาส
สาธารณะ
file://polymorphism ทำให้โปรแกรมของคุณ "ยืดหยุ่น" มากขึ้น
ฟังก์ชั่น GetDiv (Num1, Num2: Double): Double; โอเวอร์โหลด;
ฟังก์ชั่น GetDiv (Num1, Num2: จำนวนเต็ม): จำนวนเต็ม; โอเวอร์โหลด;
จบ;
การดำเนินการ
{ แผนก }
ฟังก์ชัน TDivision.GetDiv (Num1, Num2: Double): Double;
เริ่ม
พยายาม
ผลลัพธ์:=Num1/Num2;
ยกเว้น
ผลลัพธ์:=0;//จัดเตรียมกลไกการประมวลผลสัญลักษณ์แสดงหัวข้อย่อยเพื่อจัดการกับสถานการณ์ที่ตัวหารเป็น 0
จบ;
จบ;
ฟังก์ชัน TDivision.GetDiv (Num1, Num2: จำนวนเต็ม): จำนวนเต็ม;
เริ่ม
พยายาม
ผลลัพธ์:=Num1 div Num2;
ยกเว้น
ผลลัพธ์:=0;//จัดเตรียมกลไกการประมวลผลสัญลักษณ์แสดงหัวข้อย่อยเพื่อจัดการกับสถานการณ์ที่ตัวหารเป็น 0
จบ;
จบ;
จบ.
ในโค้ดข้างต้น เราใช้กลไก polymorphism ของคลาสเพื่อประมวลผลการหารเป็นการหารจำนวนเต็มและการหารที่ไม่ใช่จำนวนเต็มตามลำดับ และใช้หน้าจอการจัดการข้อยกเว้น
หากต้องการลบเคสที่เป็นเลข 0 เพื่อความปลอดภัยในการทำงาน เวลาโทร เราทำได้ดังนี้
การใช้งาน
หน่วยที่ 2;
{$R *.dfm}
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
var
แผนก:Tแผนก;
ค่า IValue: จำนวนเต็ม;
ค่า F:สองเท่า;
เริ่ม
กอง:=TDivision.Create;
ค่า IValue:=Division.GetDiv(1,2);
FValue:=Division.GetDiv(1.0,2);
ค่า IValue:=Division.GetDiv(1,0);
FValue:=Division.GetDiv(1.0,0);
ดิวิชั่นฟรี;
จบ;
6. คลาสเป็นกลไกการใช้โค้ดซ้ำ
ตัวอย่างเช่น ในข้อ 5 หากเราต้องการเพิ่มฟังก์ชัน GetAdd ให้กับคลาสนี้เพื่อดำเนินการเพิ่มเติม เราสามารถใช้การสืบทอดคลาสได้ ชอบ
เพียงแค่เขียน:
(หนึ่ง)
หน่วย หน่วยที่ 2;
อินเตอร์เฟซ
พิมพ์
TDivision=คลาส
สาธารณะ
ฟังก์ชั่น GetDiv (Num1, Num2: Double): Double; โอเวอร์โหลด;
ฟังก์ชั่น GetDiv (Num1, Num2: จำนวนเต็ม): จำนวนเต็ม; โอเวอร์โหลด;
จบ;
พิมพ์
TOการดำเนินการ=คลาส(TDivision)
สาธารณะ
ฟังก์ชั่น GetAdd (Num1, Num2: Double): Double;
จบ;
การดำเนินการ
{ แผนก }
ฟังก์ชัน TDivision.GetDiv (Num1, Num2: Double): Double;
เริ่ม
พยายาม
ผลลัพธ์:=Num1/Num2;
ยกเว้น
ผลลัพธ์:=0;
จบ;
จบ;
ฟังก์ชัน TDivision.GetDiv (Num1, Num2: จำนวนเต็ม): จำนวนเต็ม;
เริ่ม
พยายาม
ผลลัพธ์:=Num1 div Num2;
ยกเว้น
ผลลัพธ์:=0;
จบ;
จบ;
{ท็อปการดำเนินการ}
ฟังก์ชั่น TOperation.GetAdd (Num1, Num2: Double): Double;
เริ่ม
ผลลัพธ์:=Num1+Num2;
จบ;
จบ.
ที่นี่เราสืบทอดคลาสย่อย TOPeration จาก TDivision การดำเนินการสามารถมี TDivsion ได้
วิธีการสาธารณะ GetDiv มีวิธีการเฉพาะของตัวเอง GetAdd นี่คือคลาส "กินเค้กและกินมันด้วย" ที่เราเตรียมไว้ให้
วิธี "รับ" ไม่เลวเลย :)