สองวิธีในการใช้โหมด Singleton ใน Delphi
ฮาวซี
บทคัดย่อ บทความนี้จะอธิบายการใช้งาน Delphi สองครั้งในโหมดซิงเกิลตัน และทำการวิเคราะห์เชิงเปรียบเทียบ
รูปแบบการออกแบบคำสำคัญ ซิงเกิลตัน
รูปแบบซิงเกิลตันเป็นรูปแบบการออกแบบที่มีประโยชน์มาก จุดประสงค์คือเพียงสร้างอินสแตนซ์ของคลาสและจัดเตรียมจุดเข้าถึงส่วนกลางให้กับคลาสนั้น ตัวแปรร่วมทำให้วัตถุเข้าถึงได้ง่าย แต่ไม่ได้ป้องกันคุณจากการสร้างอินสแตนซ์หลายวัตถุ วัตถุประสงค์ของรูปแบบซิงเกิลตันคือเพื่อให้แน่ใจว่ามีอินสแตนซ์เดียวเท่านั้นในระหว่างวงจรชีวิตของโปรแกรม
ดูรหัสด้านล่าง:
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
var lS1 : TSingleton;
S2: ทีซิงเกิลตัน;
เริ่ม
ลอง lS1 := TSingleton.Create; ///// เรียก Constructor ของคลาส
lS2 := TSingleton.Create; ///// เรียก Constructor ของคลาส
/// ...รหัสอื่น ๆ
ในที่สุด
lS1.Free; /////ปล่อยวัตถุ
lS2.Free; /////ปล่อยวัตถุ
จบ;
จบ;
ในโค้ดข้างต้น คลาส TSingleton จะถูกสร้างอินสแตนซ์เมื่อมีการเรียกใช้ฟังก์ชัน Create เป็นครั้งแรก lS1 ชี้ไปยังที่อยู่ของออบเจ็กต์หน่วยความจำ เมื่อฟังก์ชัน TSingleton.Create ถูกเรียกใช้เป็นครั้งที่สอง วัตถุ TSingleton จะถูกเรียกใช้อีกครั้ง - จำลองสถานการณ์ lS2 ชี้ไปยังที่อยู่อื่นที่จัดสรรหน่วยความจำ รูปแบบ Singleton คือปล่อยให้คลาสรับผิดชอบในการบันทึกอินสแตนซ์เดียวเท่านั้น
ในโค้ดข้างต้น เมื่อ ls2 ถูกสร้างขึ้น มันจะชี้ไปที่วัตถุที่ ls1 ชี้ไปด้วย (นั่นคือ มันถูกจัดสรรที่อยู่หน่วยความจำเดียวกัน) ในทำนองเดียวกัน เราต้องป้องกันไม่ให้หน่วยความจำถูกปล่อยออกมาเมื่อปล่อย ls1 เนื่องจากเป็น single วัตถุยังถูกอ้างอิงโดย ls2 เพื่อให้แน่ใจว่ามีอินสแตนซ์คลาสเพียงอินสแตนซ์เดียวในระหว่างวงจรชีวิตของโปรแกรม
รหัสตัวอย่างของ C++ ใน "รูปแบบการออกแบบ" ใช้ตัวแปรสมาชิกแบบคงที่ C++ เพื่อบันทึกอินสแตนซ์ และใช้ฟังก์ชันตัวสร้างที่ได้รับการป้องกัน อย่างไรก็ตาม เนื่องจากไม่มีตัวแปรสมาชิกแบบคงที่ใน Delphi วิธีการของตัวอย่างโหมดซิงเกิลตันจึงไม่สามารถนำมาใช้ตามที่เป็นอยู่ ด้านล่างนี้เราจะวิเคราะห์หลายวิธีสำหรับ DELPHI ในการใช้โหมดซิงเกิลตัน
หนึ่ง. วิธีการขึ้นอยู่กับการแทนที่ฟังก์ชันเสมือนของ Tobject สองฟังก์ชัน
ฟังก์ชันคลาส NewInstance: TObject เสมือน;
ขั้นตอน FreeInstance เสมือน;
ฟังก์ชัน NewInstance มีหน้าที่รับผิดชอบในการจัดสรรหน่วยความจำสำหรับวัตถุเมื่อมีการสร้างวัตถุคลาส ในขณะที่ FreeInstance จะปล่อยหน่วยความจำในทางตรงกันข้าม
-
แบบแรกเรียกว่าเมื่อวัตถุถูกสร้างขึ้น และแบบหลังเรียกว่าเมื่อวัตถุถูกทำลาย
เราใช้ตัวแปรส่วนกลางสองตัวเพื่อเก็บออบเจ็กต์ซิงเกิลตันและจำนวนอ้างอิงของออบเจ็กต์
varInstance: TSingleton = ไม่มี;
RefCount : จำนวนเต็ม = 0;
หน่วยของคลาส TSingleton:
/////------------------------------------------------- -- --------------------------
-
หน่วย uSingleton;
อินเตอร์เฟซ
พิมพ์
TSingleton = คลาส (TObject)
สาธารณะ
ฟังก์ชันคลาส NewInstance: TObject; ///// แทนที่ฟังก์ชันคลาสพื้นฐาน
ขั้นตอน FreeInstance; แทนที่; ///// แทนที่ฟังก์ชันคลาสพื้นฐาน
ฟังก์ชั่นคลาส RefCount: Integer;//// ส่งคืนจำนวนอ้างอิงปัจจุบัน
จบ;
/// ประกาศตัวแปรโกลบอล
var
อินสแตนซ์: TSingleton = ไม่มี;
RefCount: จำนวนเต็ม = 0;
การดำเนินการ
{ทีซิงเกิลตัน}
ขั้นตอน TSingleton.FreeInstance;
เริ่ม
Dec( RefCount );////// ลดจำนวนการอ้างอิง
ถ้า (RefCount = 0) แล้ว////เป็น 0 หรือไม่ ถ้าเป็นเช่นนั้น ให้ปล่อยหน่วยความจำ
เริ่ม
อินสแตนซ์ := ไม่มี;
///// ปล่อยตัวแปรส่วนตัวของคลาสซิงเกิลตัน
-
FreeInstance ที่สืบทอดมา;
จบ;
จบ;
ฟังก์ชันคลาส TSingleton.NewInstance: TObject;
เริ่ม
if ( not Assigned( Instance ) ) แล้ว
เริ่ม
อินสแตนซ์ := TSingleton (สืบทอด NewInstance);
///// ตัวอย่างการเริ่มต้นตัวแปรส่วนตัว:
///// Instance.VariableName := ค่า;
จบ;
ผลลัพธ์ := อินสแตนซ์ ;
Inc( จำนวนอ้างอิง );
จบ;
ฟังก์ชันคลาส TSingleton.RefCount: จำนวนเต็ม;
เริ่ม
ผลลัพธ์ := RefCount;
จบ;
จบ.
/////------------------------------------------------- -- --------------------------
-
เมื่อตัวสร้างของ TSingleton ถูกเรียก ฟังก์ชันแทนที่ NewInstance ของเราจะถูกเรียก และ NewInstance จะจัดสรรหน่วยความจำและส่งคืนให้กับตัวสร้าง ด้วยวิธีนี้ เรารับรองว่าฟังก์ชัน Create สามารถสร้างอินสแตนซ์ของอ็อบเจ็กต์ TSingleton ได้เท่านั้น (ไม่ว่าจะเรียกกี่ครั้งก็ตาม) Create จะส่งคืนเฉพาะที่อยู่หน่วยความจำที่จัดสรรในครั้งแรกเท่านั้น) ในขณะเดียวกัน ตัวแปร RefCount จะเก็บจำนวนการอ้างอิงที่เรามีไปยังออบเจ็กต์
มาดูโค้ดทดสอบกัน
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
var
lS1, lS2: Tซิงเกิลตัน;
Ob1, Ob2: วัตถุ;
เริ่ม
lS1 := TSingleton.Create;
ShowMessage(IntToStr(RefCount)); ///// Ref_Count = 1
lS2 := TSingleton.Create;
ShowMessage(IntToStr(RefCount)); ///// Ref_Count = 2
Ob1 := TObject สร้าง;
Ob2 := Tobject.Create;
ถ้า lS1 = lS2 แล้ว
ShowMessage('ที่อยู่เท่ากัน') ///// lS1 = lS2
อื่น
ShowMessage('ที่อยู่ไม่เท่ากัน');
ถ้า Ob1 = Ob2 แล้ว
ShowMessage('ที่อยู่เท่ากัน')
อื่น
ShowMessage('ที่อยู่ไม่เท่ากัน'); ///// Ob1 <> Ob2
จบ;
เมื่อโปรแกรมเรียกใช้ destructor (นั่นคือ เมื่อเรียกใช้ฟังก์ชัน FREE) destructor จะเรียกใช้ฟังก์ชัน FreeInstance เพื่อปล่อยหน่วยความจำที่จัดสรรโดย Constructor ฟังก์ชัน FreeInstance ของ Override ช่วยให้มั่นใจได้ว่าหน่วยความจำของออบเจ็กต์โหมดซิงเกิลตันจะถูกปล่อยออกมาเมื่อจำนวนการอ้างอิงถึงศูนย์เท่านั้น
นี่คือรหัสทดสอบของเรา:
var
lS1: Tซิงเกิลตัน;
lS2: Tซิงเกิลตัน;
เริ่ม
พยายาม
lS1 := TSingleton.Create; ///// เรียก Constructor ของคลาส
lS2 := TSingleton.Create; ///// เรียก Constructor ของคลาส
/// ...รหัสอื่น ๆ
ในที่สุด
lS1.Free; ///// ในที่นี้เราจะเรียก FreeInstance ที่กำหนดโดยการแทนที่ของเราก่อน
/////เนื่องจาก RefCount คือ 1 หลังจากที่ลดลง 1 ในขณะนี้ วัตถุซิงเกิลตันจึงยังไม่ถูกเผยแพร่
lS2.Free; /////dec(RefCount)= 0 ปล่อยอ็อบเจ็กต์ซิงเกิลตัน
จบ;
จบ;
วิธีการนำรูปแบบซิงเกิลตันไปใช้ข้างต้นเป็นวิธีที่ดีในการตระหนักว่าคลาสนั้นมีหน้าที่รับผิดชอบในการบันทึกอินสแตนซ์เฉพาะของตัวเอง (โดยการสกัดกั้นคำขอเพื่อสร้างออบเจ็กต์ใหม่ - อ้างถึง "รูปแบบการออกแบบ" ไม่มีข้อจำกัดพิเศษในการใช้งาน ของคลาส TSingleton - — โปรแกรมเมอร์สามารถเรียกใช้ฟังก์ชัน Create และ Free ได้ตามต้องการ
ข้อเสียของโหมดนี้คือคลาส TSingleton ไม่สามารถสืบทอดเป็นคลาสพาเรนต์เพื่อสร้างคลาสย่อยได้ หากการสืบทอดสร้างคลาสย่อยสองคลาส จะมีการสร้างออบเจ็กต์เดียวเท่านั้นในระหว่างการสร้าง
ขั้นตอน TForm1.Button1Click (ผู้ส่ง: TObject);
var
lS1: หมวดหมู่ย่อยหนึ่ง;
lS2: หมวดหมู่ย่อยสอง;
เริ่ม
lS1 := คลาสย่อย 1.สร้าง;
lS2 := Subclass 2.Create; ///// Subclass 2 จะไม่ถูกสร้างขึ้น lS2 จะชี้ไปที่หน่วยความจำที่ lS1 ชี้ไป
/////นั่นคือ lS1 = lS2end;
สอง. การใช้ตัวอย่าง Delphi ใน "รูปแบบการออกแบบ"
ตัวอย่างการใช้งาน "รูปแบบการออกแบบ" คือการควบคุมอินสแตนซ์ออบเจ็กต์เดียวเท่านั้นผ่านฟังก์ชันตัวสร้างส่วนตัว อย่างไรก็ตาม การใช้โค้ด C++ ที่กำหนดไม่ได้ระบุวิธีการเผยแพร่ออบเจ็กต์ ฟังก์ชัน Create ไม่สามารถแปรรูปได้ใน Delphi เรากำหนดฟังก์ชันใหม่เพื่อแทนที่ฟังก์ชัน Create และป้องกันฟังก์ชัน Create ของคลาสพาเรนต์ รหัสมีดังนี้
-
/////------------------------------------------------- -- --------------------------
-
หน่วย uSingletonUnit;
อินเตอร์เฟซ
การใช้งาน
คลาส, SysUtils;
พิมพ์
TCSingleton = class (TComponent) ///// สืบทอดมาจากคลาส Tcomponent
ส่วนตัว
ตัวสร้าง CreateInstance (AOwner: TComponent); ///// ส่งพารามิเตอร์ Owner
///// ด้วยวิธีนี้ อ็อบเจ็กต์คลาส TCSingleton จะถูกทำลายพร้อมกับเจ้าของ (เจ้าของมีหน้าที่รับผิดชอบในการทำลายอ็อบเจ็กต์ TCSingleton)
สาธารณะ
ตัวสร้างสร้าง (AOwner: TComponent);
ฟังก์ชันคลาสอินสแตนซ์ (AOwner: TComponent): TCSingleton;
จบ;
var
gCSingleton: TCSingleton; ///// ตัวแปรโกลบอล
การดำเนินการ
{ทีซีซิงเกิลตัน}
ตัวสร้าง TCSingleton.Create (AOwner: TComponent);
เริ่ม
///// ป้องกันฟังก์ชันของฟังก์ชัน Create
เพิ่ม Exception.CreateFmt('เข้าถึงคลาส %s ผ่านอินสแตนซ์เท่านั้น',
[ชื่อคลาส]);
จบ;
ตัวสร้าง TCSingleton.CreateInstance (AOwner: TComponent);
เริ่ม
///// Constructor ที่กำหนดใหม่เป็นแบบส่วนตัว
สืบทอดสร้าง (AOwner);
จบ;
ฟังก์ชันคลาส TCSingleton.Instance (AOwner: TComponent): TCSingleton;
เริ่ม
หากไม่ได้รับมอบหมาย (gCSingleton) แล้ว
gCSingleton := TCSingleton.CreateInstance (AOwner);
ผลลัพธ์ := gCSingleton;
จบ;
จบ.
/////------------------------------------------------- ------------------------------/
-
ในระหว่างการใช้คลาสการใช้งานข้างต้น โปรแกรมเมอร์ไม่จำเป็นต้องพิจารณาถึงการทำลายออบเจ็กต์โหมดซิงเกิลตัน ไม่สามารถเรียก Create ได้ คุณต้องเรียกใช้ฟังก์ชัน Instance เพื่อรับอินสแตนซ์ของออบเจ็กต์ และส่งเจ้าของซิงเกิลตันเป็นพารามิเตอร์ไปยังฟังก์ชัน วิธีการนำไปใช้นี้สามารถสืบทอดเป็นคลาสพื้นฐานและใช้ในรูปแบบซิงเกิลตันของสถานะ (ดูข้อมูลอ้างอิงที่ 4) เพื่อให้เกิดความหลากหลายในเวลาดำเนินการ
สาม. บทสรุป
การใช้งานโหมด Singleton ของ Delphi สามารถพบได้บนอินเทอร์เน็ต มีวิธีการใช้งานอื่น ๆ สองวิธีในบทความนี้
ธรรมดาที่สุดและเรียบง่าย ในขณะเดียวกัน แนวคิดของวิธีการอื่นๆ ก็คล้ายคลึงกับสองวิธีข้างต้นเช่นกัน