ทางเลือกในรูปแบบซิงเกิล


27

ฉันได้อ่านความคิดเห็นที่แตกต่างกันเกี่ยวกับรูปแบบซิงเกิล บางคนยืนยันว่าควรหลีกเลี่ยงค่าใช้จ่ายและอื่น ๆ ที่สามารถเป็นประโยชน์ในบางสถานการณ์

สถานการณ์หนึ่งที่ฉันใช้ singletons คือเมื่อฉันต้องการโรงงาน (สมมติว่าวัตถุประเภท f) เพื่อสร้างวัตถุของคลาส A แน่นอนโรงงานถูกสร้างขึ้นครั้งเดียวโดยใช้พารามิเตอร์การกำหนดค่าบางอย่างจากนั้นจะใช้แต่ละครั้งที่วัตถุของ พิมพ์ A ถูกสร้างขึ้น ดังนั้นทุกส่วนของรหัสที่ต้องการอินสแตนซ์ A ดึงค่าซิงเกิลตันและสร้างอินสแตนซ์ใหม่เช่น

F& f                   = F::instance();
boost::shared_ptr<A> a = f.createA();

ดังนั้นสถานการณ์ทั่วไปของฉันก็คือ

  1. ฉันต้องการอินสแตนซ์เดียวของคลาสอย่างใดอย่างหนึ่งด้วยเหตุผลการปรับให้เหมาะสม (ฉันไม่ต้องการวัตถุหลายโรงงาน) หรือสำหรับการแบ่งปันสถานะทั่วไป (เช่นโรงงานทราบจำนวนอินสแตนซ์ของ A ที่ยังคงสามารถสร้างได้)
  2. ฉันต้องการวิธีการเข้าถึง f ของอินสแตนซ์นี้ในสถานที่ต่าง ๆ ของรหัส

ฉันไม่สนใจในการอภิปรายว่ารูปแบบนี้ดีหรือไม่ดี แต่สมมติว่าฉันต้องการหลีกเลี่ยงการใช้ซิงเกิลตันฉันสามารถใช้รูปแบบอื่นแบบใดได้บ้าง

แนวคิดที่ฉันมีคือ (1) นำวัตถุของโรงงานออกจากรีจิสตรีหรือ (2) เพื่อสร้างโรงงานในบางช่วงระหว่างการเริ่มต้นโปรแกรมแล้วส่งโรงงานเป็นพารามิเตอร์

ในโซลูชัน (1) ตัวรีจิสตรีนั้นเป็นซิงเกิลดังนั้นฉันเพิ่งเปลี่ยนปัญหาการไม่ใช้ซิงเกิลตันจากโรงงานไปยังรีจิสตรี

ในกรณี (2) ฉันต้องการบางแหล่งเริ่มต้น (วัตถุ) จากที่วัตถุโรงงานมาดังนั้นฉันกลัวว่าฉันจะถอยกลับไปยังซิงเกิลอื่นอีกครั้ง (วัตถุที่ให้อินสแตนซ์จากโรงงานของฉัน) โดยการติดตามห่วงโซ่ของซิงเกิลนี้กลับฉันสามารถลดปัญหาให้เหลือหนึ่งซิงเกิล (ทั้งแอปพลิเคชัน) โดยที่ซิงเกิลตันอื่นทั้งหมด ได้รับการจัดการโดยตรงหรือโดยอ้อม

ตัวเลือกสุดท้ายนี้ (ใช้ซิงเกิลตันเริ่มต้นหนึ่งตัวที่สร้างวัตถุที่ไม่ซ้ำกันอื่น ๆ ทั้งหมดและอัดฉีดซิงเกิลอื่นทั้งหมดในตำแหน่งที่ถูกต้อง) เป็นวิธีแก้ปัญหาที่ยอมรับหรือไม่? นี่เป็นวิธีแก้ปัญหาที่แนะนำโดยปริยายเมื่อมีคำแนะนำว่าไม่ควรใช้ซิงเกิลตันหรือมีคำตอบอื่นใดอีกเช่นในตัวอย่างที่แสดงด้านบน

แก้ไข

เนื่องจากฉันคิดว่าประเด็นคำถามของฉันถูกเข้าใจผิดโดยบางคนนี่เป็นข้อมูลเพิ่มเติม ตามที่อธิบายไว้ที่นี่คำว่า ซิงเกิลสามารถระบุ (a) คลาสที่มีวัตถุอินสแตนซ์เดียวและ (b) รูปแบบการออกแบบที่ใช้ในการสร้างและเข้าถึงวัตถุดังกล่าว

เพื่อให้สิ่งต่าง ๆ ชัดเจนขึ้นให้เราใช้คำเฉพาะสำหรับ (a) และ รูปแบบซิงเกิลสำหรับ (b) ดังนั้นฉันจึงรู้ว่ารูปแบบซิงเกิลและการฉีดขึ้นต่อกันคืออะไร (BTW เมื่อเร็ว ๆ นี้ฉันใช้ DI หนักเพื่อลบอินสแตนซ์ของรูปแบบซิงเกิลออกจากโค้ดที่ฉันใช้งานอยู่)

จุดของฉันคือว่าถ้ากราฟวัตถุทั้งหมดถูกสร้างอินสแตนซ์จากวัตถุเดียวที่อาศัยอยู่บนสแต็กของวิธีการหลักจะมีความจำเป็นในการเข้าถึงวัตถุที่ไม่ซ้ำกันบางอย่างผ่านรูปแบบซิงเกิล

คำถามของฉันคือการมีการสร้างกราฟวัตถุที่สมบูรณ์และการเดินสายขึ้นอยู่กับวิธีการหลัก (เช่นผ่านกรอบ DI ที่ทรงพลังบางอย่างที่ไม่ได้ใช้รูปแบบตัวเอง) เป็นโซลูชั่นฟรีรูปแบบซิงเกิลเดียว


8
การฉีดพึ่งพา ...
ฟอลคอน

1
@ เหยี่ยว: ฉีดพึ่งพา ... อะไร DI ไม่ทำอะไรเลยเพื่อแก้ไขปัญหานี้ แต่ก็มักจะให้วิธีที่สะดวกในการซ่อนมันไว้
สาธารณรัฐประชาธิปไตยประชาชนลาว

@ Falcon คุณหมายถึงคอนเทนเนอร์ IoC หรือไม่ สิ่งนี้จะช่วยให้คุณแก้ไขการขึ้นต่อกันในบรรทัดเดียว เช่นการพึ่งพา. แก้ไข <IFoo> (); or Dependency.Resolve <IFoo> () .DoSomething ();
CodeART

นี่เป็นคำถามที่ค่อนข้างเก่าและตอบแล้ว ฉันยังคงเป็นการดีถ้าจะเชื่อมโยงสิ่งนี้: stackoverflow.com/questions/162042/ …
TheSilverBullet

คำตอบ:


7

ตัวเลือกที่สองของคุณเป็นวิธีที่ดีในการไป - มันเป็นชนิดของการพึ่งพาการฉีดซึ่งเป็นรูปแบบที่ใช้ในการแบ่งปันสถานะในโปรแกรมของคุณเมื่อคุณต้องการหลีกเลี่ยงซิงเกิลและตัวแปรทั่วโลก

คุณไม่สามารถหลีกเลี่ยงความจริงที่ว่ามีบางสิ่งในการสร้างโรงงานของคุณ หากสิ่งนั้นเกิดขึ้นเป็นแอปพลิเคชันดังนั้นไม่ว่าจะเป็น จุดสำคัญคือโรงงานของคุณไม่ควรสนใจว่าวัตถุใดที่สร้างขึ้นและวัตถุที่ได้รับจากโรงงานไม่ควรขึ้นอยู่กับว่าโรงงานมาจากไหน อย่าให้วัตถุของคุณได้รับตัวชี้ไปยังแอปพลิเคชันซิงเกิลและขอให้โรงงาน ให้แอปพลิเคชันของคุณสร้างโรงงานและมอบให้กับวัตถุเหล่านั้นที่จำเป็นต้องใช้


17

วัตถุประสงค์ของซิงเกิลตันคือบังคับให้มีอินสแตนซ์เดียวเท่านั้นที่สามารถมีอยู่ภายในขอบเขตที่แน่นอน ซึ่งหมายความว่าซิงเกิลตันมีประโยชน์ถ้าคุณมีเหตุผลที่ดีในการบังคับใช้พฤติกรรมแบบซิงเกิล ในทางปฏิบัตินี่เป็นกรณีที่ไม่ค่อยและการประมวลผลแบบมัลติและเธรดแบบมัลติเธรดจะทำให้ความหมายของ 'มีเอกลักษณ์' ชัดเจนขึ้น - มันเป็นหนึ่งอินสแตนซ์ต่อหนึ่งเครื่องต่อกระบวนการต่อเธรดต่อคำขอหรือไม่ การใช้งานซิงเกิลตันของคุณช่วยดูแลสภาพการแข่งขันไหม

แทนที่จะใช้ซิงเกิลฉันชอบที่จะเลือกใช้อย่างใดอย่างหนึ่ง

  • อินสแตนซ์ในท้องถิ่นที่มีอายุสั้นเช่นสำหรับโรงงาน: คลาสโรงงานทั่วไปมีสถานะจำนวนน้อยที่สุดหากมีและไม่มีเหตุผลที่แท้จริงที่จะทำให้พวกเขามีชีวิตอยู่หลังจากที่พวกเขาทำหน้าที่ตามวัตถุประสงค์แล้ว ค่าใช้จ่ายในการสร้างและการลบชั้นเรียนนั้นไม่ต้องกังวลใน 99% ของสถานการณ์ในโลกแห่งความเป็นจริงทั้งหมด
  • ส่งตัวอย่างไปรอบ ๆ เช่นสำหรับผู้จัดการทรัพยากร: สิ่งเหล่านี้ต้องมีอายุการใช้งานยาวนานเนื่องจากการโหลดทรัพยากรมีราคาแพงและคุณต้องการเก็บไว้ในหน่วยความจำ แต่ไม่มีเหตุผลใดที่จะป้องกันไม่ให้มีการสร้างอินสแตนซ์เพิ่มเติม อาจเป็นเรื่องที่สมควรที่จะมีผู้จัดการทรัพยากรคนที่สองเมื่อไม่กี่เดือนที่ผ่านมา ...

เหตุผลที่ว่าซิงเกิลตันนั้นเป็นรัฐระดับโลกที่แฝงตัวอยู่ซึ่งหมายความว่ามันจะแนะนำการมีเพศสัมพันธ์ในระดับสูงตลอดทั้งแอปพลิเคชัน - ส่วนใด ๆ ของรหัสของคุณสามารถคว้าอินสแตนซ์ซิงเกิลตันจากที่ใดก็ได้ ถ้าคุณใช้วัตถุภายในเครื่องหรือผ่านอินสแตนซ์รอบ ๆ คุณมีการควบคุมที่มากขึ้นและคุณสามารถทำให้ขอบเขตของคุณเล็กและพึ่งพาของคุณแคบ


3
นี่เป็นคำตอบที่ดีมาก แต่ฉันไม่ได้ถามจริง ๆ ว่าทำไมฉันจึงควร / ไม่ควรใช้รูปแบบซิงเกิล ฉันรู้ปัญหาที่เกิดขึ้นกับสถานะโลก อย่างไรก็ตามหัวข้อของคำถามคือทำอย่างไรถึงจะมีอ็อบเจกต์ที่ไม่ซ้ำกัน
Giorgio

3

คนส่วนใหญ่ (รวมถึงคุณ) เข้าใจผิดอย่างสมบูรณ์ว่ารูปแบบของซิงเกิลจริงๆคืออะไร รูปแบบ Singleton หมายถึงว่ามีอินสแตนซ์เดียวของคลาสอยู่และมีกลไกบางอย่างสำหรับโค้ดทั่วทั้งแอปพลิเคชันเพื่อรับการอ้างอิงไปยังอินสแตนซ์นั้น

ในหนังสือ GoF วิธีสแตติกจากโรงงานที่ส่งคืนการอ้างอิงไปยังฟิลด์สแตติกเป็นเพียงตัวอย่างว่ากลไกนั้นอาจมีลักษณะอย่างไรและอีกวิธีหนึ่งที่มีข้อบกพร่องร้ายแรง น่าเสียดายที่ทุกคนและสุนัขของพวกเขาเข้ายึดกลไกนั้นและคิดว่าเป็นสิ่งที่ซิงเกิ้ลเป็นเรื่องเกี่ยวกับ

ทางเลือกที่คุณอ้างถึงก็คือ Singletons ด้วยกลไกที่แตกต่างกันในการรับการอ้างอิง (2) ส่งผลให้เกิดการผ่านไปมากเกินไปอย่างชัดเจนเว้นแต่ว่าคุณต้องการการอ้างอิงในเพียงไม่กี่แห่งใกล้กับรูทของ call stack (1) ดูเหมือนว่ากรอบการฉีดที่ขึ้นต่อกันของน้ำมันดิบ - ดังนั้นทำไมไม่ใช้อย่างใดอย่างหนึ่ง

ข้อดีคือเฟรมเวิร์ก DI ที่มีอยู่นั้นมีความยืดหยุ่นมีประสิทธิภาพและผ่านการทดสอบเป็นอย่างดี พวกเขาสามารถทำได้มากกว่าจัดการซิงเกิล อย่างไรก็ตามเพื่อใช้ความสามารถอย่างเต็มที่พวกเขาจะทำงานได้ดีที่สุดหากคุณจัดโครงสร้างแอปพลิเคชันของคุณด้วยวิธีการบางอย่างซึ่งไม่สามารถทำได้เสมอไป: โดยปกติแล้วมีวัตถุกลางที่ได้มาจากกรอบ DI และมีการอ้างอิงทั้งหมด ดำเนินการแล้ว

แก้ไข: ในที่สุดทุกอย่างขึ้นอยู่กับวิธีการหลักอยู่แล้ว แต่คุณพูดถูก: วิธีเดียวที่จะหลีกเลี่ยงการใช้โกลบอล / สแตติกทั้งหมดคือการตั้งค่าทุกอย่างจากวิธีหลัก โปรดทราบว่า DI ได้รับความนิยมมากที่สุดในสภาพแวดล้อมเซิร์ฟเวอร์ที่เมธอดหลักทึบและตั้งค่าเซิร์ฟเวอร์และรหัสแอปพลิเคชันประกอบด้วยการโทรกลับที่ถูกสร้างและเรียกใช้โดยรหัสเซิร์ฟเวอร์ แต่เฟรมเวิร์ก DI ส่วนใหญ่ยังอนุญาตให้เข้าถึงรีจิสตรีส่วนกลางโดยตรงเช่น ApplicationContext ของ Spring สำหรับ "กรณีพิเศษ"

โดยพื้นฐานแล้วสิ่งที่ดีที่สุดที่ผู้คนได้ค้นพบคือการผสมผสานระหว่างสองทางเลือกที่คุณกล่าวถึงอย่างชาญฉลาด


คุณหมายถึงว่าการฉีดความคิดพื้นฐานนั้นเป็นวิธีแรกที่ฉันร่างไว้ข้างต้น (เช่นใช้รีจิสตรี) ในที่สุดความคิดพื้นฐานในที่สุด?
Giorgio

@Giorgio: DI มักจะรวมถึงรีจิสทรีเช่นกัน แต่ประเด็นหลักที่ทำให้ดีขึ้นก็คือแทนที่จะทำการค้นหารีจิสทรีทุกที่ที่คุณต้องการวัตถุคุณมีวัตถุกลางที่มีการขนส่งตามที่ฉันเขียนไว้ด้านบน
Michael Borgwardt

@Giorgio: ง่ายกว่าที่จะเข้าใจด้วยตัวอย่างที่เป็นรูปธรรม: สมมติว่าคุณมีแอป Java EE ตรรกะส่วนหน้าของคุณอยู่ในวิธีการดำเนินการของ JSF Managed Bean เมื่อผู้ใช้กดปุ่มคอนเทนเนอร์ JSF จะสร้างอินสแตนซ์ของ Managed Bean และเรียกวิธีการดำเนินการ แต่ก่อนหน้านั้นส่วนประกอบ DI จะดูที่ฟิลด์ของคลาส Managed Bean และสิ่งที่มีคำอธิบายประกอบจะถูกบรรจุด้วยการอ้างอิงถึงวัตถุบริการตามการกำหนดค่า DI และบริการ objecs เหล่านั้นมีสิ่งเดียวกันเกิดขึ้นกับพวกเขา . วิธีการดำเนินการนั้นสามารถเรียกบริการ
Michael Borgwardt

บางคน (รวมถึงคุณ) ไม่ได้อ่านคำถามอย่างรอบคอบและเข้าใจผิดว่ามีการถามอะไร
Giorgio

1
@Giorgio: ไม่มีอะไรในคำถามเดิมของคุณที่ระบุว่าคุณคุ้นเคยกับการฉีดยา และดูคำตอบที่อัปเดต
Michael Borgwardt

3

มีทางเลือกไม่กี่:

ฉีดพึ่งพา

วัตถุทุกชิ้นมีการส่งต่อไปยังมันเมื่อมันถูกสร้างขึ้น โดยทั่วไปแล้วทั้งเฟรมเวิร์กหรือคลาสโรงงานจำนวนเล็กน้อยมีหน้าที่สร้างและเชื่อมโยงวัตถุ

Service Registry

ทุกวัตถุถูกส่งผ่านวัตถุรีจิสตรีการบริการ มันมีวิธีการขอวัตถุต่าง ๆ ที่ให้บริการที่แตกต่าง เฟรมเวิร์ก Android ใช้รูปแบบนี้

ผู้จัดการรูท

มีวัตถุเดียวซึ่งเป็นรากของกราฟวัตถุ โดยการติดตามลิงก์จากวัตถุนี้วัตถุอื่น ๆ สามารถพบได้ในที่สุด รหัสมีแนวโน้มที่จะมีลักษณะดังนี้:

GetSomeManager()->GetRootManager()->GetAnotherManager()->DoActualThingICareAbout()

นอกเหนือจากการเยาะเย้ยแล้ว DI ใดที่มีประโยชน์ในการใช้ซิงเกิลตัน? นี่เป็นคำถามที่บั๊กฉันมานานมาก สำหรับผู้จัดการรีจิสตรีและรูทพวกเขาไม่ได้ใช้งานทั้งแบบซิงเกิลหรือผ่าน DI หรือไม่? (อันที่จริงคอนเทนเนอร์ IoC เป็นรีจิสตรีใช่มั้ย)
Konrad Rudolph

1
@KonradRudolph โดย DI การขึ้นต่อกันนั้นชัดเจนและวัตถุไม่ได้ตั้งสมมติฐานเกี่ยวกับวิธีการจัดหาทรัพยากรที่กำหนด ด้วย singletons การพึ่งพานั้นมีความหมายโดยนัยและการใช้ทุก ๆ ครั้งทำให้มีการสันนิษฐานว่าจะมีเพียงวัตถุเดียวเท่านั้น
Winston Ewert

@ KonradRudolph เป็นวิธีการอื่น ๆ ที่ดำเนินการโดยใช้ Singletons หรือ DI? ใช่และไม่ใช่ในที่สุดคุณจะต้องผ่านวัตถุหรือเก็บวัตถุในสถานะโกลบอล แต่มีความแตกต่างเล็กน้อยในวิธีที่คุณทำเช่นนี้ ทั้งสามเวอร์ชันที่กล่าวถึงข้างต้นทั้งหมดหลีกเลี่ยงการใช้สถานะโกลบอล แต่ลักษณะที่ end-code ได้รับการอ้างอิงนั้นแตกต่างกัน
Winston Ewert

การใช้ singletons ถือว่าไม่มีวัตถุเดียวหาก singleton ใช้อินเตอร์เฟส (หรือแม้ว่าจะไม่ใช่) และคุณส่งอินสแตนซ์ singleton ไปยังเมธอดแทนการเคียวรีSingleton::instanceทุกหนทุกแห่งในรหัสลูกค้า
Konrad Rudolph

@ KonradRudolph คุณใช้ไฮบริดสลีตัน / DI แบบไฮบริดจริงๆ ด้านคนเจ้าระเบียบของฉันคิดว่าคุณควรไปหาแนวทาง DI ที่บริสุทธิ์ อย่างไรก็ตามปัญหาส่วนใหญ่ของซิงเกิลไม่ได้นำไปใช้จริง
Winston Ewert

1

หากความต้องการของคุณจากซิงเกิลสามารถต้มลงไปยังฟังก์ชั่นเดียวทำไมไม่ใช้ฟังก์ชั่นโรงงานธรรมดา? ฟังก์ชั่นทั่วโลก (อาจเป็นวิธีการคงที่ของคลาส F ในตัวอย่างของคุณ) เป็นซิงเกิลตันโดยมีเอกลักษณ์บังคับใช้โดยคอมไพเลอร์และลิงเกอร์

class Factory
{
public:
    static Object* createObject(...);
};

Object* obj = Factory::createObject(...);

เป็นที่ยอมรับว่าสิ่งนี้จะหยุดลงเมื่อการทำงานของซิงเกิลตันไม่สามารถลดลงเป็นการเรียกใช้ฟังก์ชันเดียวได้แม้ว่าอาจจะมีฟังก์ชั่นที่เกี่ยวข้องกับชุดเล็ก ๆ

ทุกอย่างที่กล่าวไว้ในข้อที่ 1 และ 2 ของคุณทำให้ชัดเจนว่าคุณต้องการสิ่งใดสิ่งหนึ่ง ขึ้นอยู่กับนิยามของรูปแบบซิงเกิลของคุณว่าคุณใช้อยู่แล้วหรือใกล้มาก ฉันไม่คิดว่าคุณจะมีอย่างใดอย่างหนึ่งโดยไม่ต้องเป็นซิงเกิลหรืออย่างน้อยก็ใกล้เคียง มันใกล้ความหมายของซิงเกิลมากเกินไป

ตามที่คุณแนะนำในบางจุดคุณต้องมีสิ่งใดสิ่งหนึ่งดังนั้นปัญหาอาจไม่ได้มีอินสแตนซ์เดียวของบางสิ่ง แต่ทำตามขั้นตอนเพื่อป้องกันการทารุณกรรม (หรืออย่างน้อยท้อ การย้ายสถานะออกจาก "ซิงเกิล" และพารามิเตอร์ให้มากที่สุดเป็นการเริ่มต้นที่ดี การ จำกัด อินเทอร์เฟซให้แคบลงยังช่วยได้เช่นกันเนื่องจากมีโอกาสน้อยลงสำหรับการใช้งานในทางที่ผิด บางครั้งคุณต้องดูดมันและทำให้ซิงเกิลนั้นมีความแข็งแกร่งเช่น heap หรือระบบไฟล์


4
โดยส่วนตัวแล้วฉันเห็นว่านี่เป็นเพียงการนำรูปแบบซิงเกิลมาใช้ใหม่ ในกรณีนี้อินสแตนซ์ซิงเกิลเป็นคลาสเอง โดยเฉพาะ: มันมีข้อเสียเช่นเดียวกับซิงเกิล "ของจริง"
โจอาคิมซาวเออร์

@ Randall Cook: ฉันคิดว่าคำตอบของคุณรวบรวมประเด็นของฉัน ฉันอ่านบ่อยมากว่าซิงเกิลนั้นแย่มากและควรหลีกเลี่ยงค่าใช้จ่ายทั้งหมด ฉันเห็นด้วยกับสิ่งนี้ แต่ในทางกลับกันฉันคิดว่าเป็นไปไม่ได้ที่จะหลีกเลี่ยงได้ในทางเทคนิค เมื่อคุณต้องการ "สิ่งใดสิ่งหนึ่ง" คุณต้องสร้างมันที่ไหนสักแห่งและเข้าถึงมันในบางวิธี
Giorgio

1

หากคุณกำลังใช้ภาษาหลายภาษาเช่น C ++ หรือ Python ทางเลือกหนึ่งของคลาส singleton คือชุดของฟังก์ชัน / ตัวแปรที่อยู่ในเนมสเปซ

แนวคิดการพูดไฟล์ C ++ ที่มีตัวแปรโกลบอลอิสระฟังก์ชันโกลบอลอิสระและตัวแปรสแตติกที่ใช้สำหรับการซ่อนข้อมูลทั้งหมดถูกรวมอยู่ในเนมสเปซ

มันจะพังถ้าคุณต้องการรับมรดก ฉันเคยเห็นซิงเกิลมากมายที่น่าจะดีกว่านี้


0

การห่อหุ้มซิงเกิลตัน

สถานการณ์กรณี แอปพลิเคชันของคุณคือ Object Oriented และต้องการซิงเกิลเฉพาะ 3 หรือ 4 ตัวแม้ว่าคุณจะมีคลาสมากขึ้นก็ตาม

ก่อนหน้าตัวอย่าง (C ++ เช่น pseudocode):

// network info
class Session {
  // ...
};

// current user connection info
class CurrentUser {
  // ...
};

// configuration upon user
class UserConfiguration {
  // ...
};

// configuration upon P.C.,
// example: monitor resolution
class TerminalConfiguration {
  // ...
};

วิธีหนึ่งคือการลบบางส่วน (ทั่วโลก) ซิงเกิลบางส่วนโดยการห่อหุ้มพวกเขาทั้งหมดให้เป็นซิงเกิลที่มีเอกลักษณ์ซึ่งมีในฐานะสมาชิก

วิธีนี้แทนที่จะเป็น "ซิงเกิลหลาย ๆ ตัวเข้าใกล้แทนที่จะไม่มีซิงเกิลเลยเข้าใกล้" เรามี "แค็ปซูลทั้งหมดเป็นหนึ่งเดียวเข้าใกล้"

หลังจากตัวอย่าง (C ++ เช่น pseudocode):

// network info
class NetworkInfoClass {
  // ...
};

// current user connection info
class CurrentUserClass {
  // ...
};

// configuration upon user
// favorite options menues
class UserConfigurationClass {
  // ...
};

// configuration upon P.C.,
// example: monitor resolution
class TerminalConfigurationClass {
  // ...
};

// this class is instantiated as a singleton
class SessionClass {
  public: NetworkInfoClass NetworkInfo;
  public: CurrentUserClass CurrentUser;
  public: UserConfigurationClass UserConfiguration;
  public: TerminalConfigurationClass TerminalConfiguration;

  public: static SessionClass Instance();
};

โปรดทราบว่าตัวอย่างเป็นเหมือน pseudocode และไม่สนใจข้อบกพร่องเล็ก ๆ น้อย ๆ หรือข้อผิดพลาดทางไวยากรณ์และพิจารณาวิธีแก้ปัญหาสำหรับคำถาม

นอกจากนี้ยังมีสิ่งอื่น ๆ ที่ควรพิจารณาเนื่องจากภาษาการเขียนโปรแกรมที่ใช้อาจมีผลต่อการใช้งานการใช้งานแบบซิงเกิลหรือแบบซิงเกิลที่ไม่ใช่แบบซิงเกิล

ไชโย


0

ข้อกำหนดดั้งเดิมของคุณ:

ดังนั้นสถานการณ์ทั่วไปของฉันก็คือ

  1. ฉันต้องการอินสแตนซ์เดียวของคลาสอย่างใดอย่างหนึ่งด้วยเหตุผลการปรับให้เหมาะสม (ฉันไม่ต้องการ> วัตถุจากโรงงานหลายรายการ) หรือสำหรับการแบ่งปันสถานะทั่วไป (เช่นโรงงานทราบว่าอินสแตนซ์ของ A จำนวนเท่าใดที่ยังสามารถสร้างได้)
  2. ฉันต้องการวิธีการเข้าถึง f ของอินสแตนซ์นี้ในสถานที่ต่าง ๆ ของรหัส

อย่าเข้าแถวกับคำจำกัดความของซิงเกิล (และสิ่งที่คุณควรอ้างถึงในภายหลัง) จาก GoF (เวอร์ชั่นของฉันคือ 1995) หน้า 127:

ตรวจสอบให้แน่ใจว่าคลาสมีเพียงอินสแตนซ์เดียวและให้จุดเข้าถึงส่วนกลางแก่คลาสนั้น

หากคุณต้องการเพียงหนึ่งอินสแตนซ์นั่นไม่ได้ขัดขวางคุณในการสร้างมากขึ้น

ถ้าคุณต้องการที่เดียวเช่นสามารถเข้าถึงได้ทั่วโลกแล้วทำให้อินสแตนซ์ทั่วโลกสามารถเข้าถึงเดียว ไม่จำเป็นต้องมีรูปแบบชื่อสำหรับทุกสิ่งที่คุณทำ และใช่อินสแตนซ์ที่เข้าถึงได้ทั่วโลกเดียวมักจะไม่ดี การให้ชื่อพวกเขาไม่ได้ทำให้พวกเขาไม่ดีน้อย

เพื่อหลีกเลี่ยงการเข้าถึงทั่วโลกร่วมกัน 'ทำตัวอย่างและส่งต่อ' หรือ 'ให้เจ้าของวัตถุสองชิ้นติดกาวเข้าด้วยกัน' วิธีการทำงานได้ดี


0

วิธีการเกี่ยวกับการใช้คอนเทนเนอร์ IoC? ด้วยความคิดบางอย่างคุณสามารถจบลงด้วยบางสิ่งในบรรทัดของ:

Dependency.Resolve<IFoo>(); 

คุณจะต้องระบุว่าการใช้งานของ IFooจะใช้เมื่อเริ่มต้น แต่นี่เป็นการกำหนดค่าแบบครั้งเดียวซึ่งคุณสามารถแทนที่ได้ในภายหลัง อายุการใช้งานของอินสแตนซ์ที่ได้รับการแก้ไขสามารถควบคุมได้โดยคอนเทนเนอร์ IoC

วิธีโมฆะซิงเกิลแบบคงที่สามารถถูกแทนที่ด้วย:

Dependency.Resolve<IFoo>().DoSomething();

วิธี singleton getter getter สามารถถูกแทนที่ด้วย:

var result = Dependency.Resolve<IFoo>().GetFoo();

ตัวอย่างเกี่ยวข้องกับ. NET แต่ฉันมั่นใจว่าสามารถคล้ายกันมากในภาษาอื่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.