อะไรคือสิ่งที่ไม่ดีเกี่ยวกับซิงเกิลตัน? [ปิด]


1982

รูปแบบเดี่ยวเป็นสมาชิกชำระเต็มของGoF 's หนังสือรูปแบบแต่มันดูเหมือนว่าเมื่อเร็ว ๆ นี้ค่อนข้างกำพร้าโลกนักพัฒนา ฉันยังคงใช้ singletons ค่อนข้างมากโดยเฉพาะอย่างยิ่งสำหรับคลาสโรงงานและในขณะที่คุณต้องระวังเล็กน้อยเกี่ยวกับปัญหามัลติเธรด

Stack Overflow โดยเฉพาะดูเหมือนว่าจะถือว่าทุกคนยอมรับว่าซิงเกิลตันนั้นชั่วร้าย ทำไม?

โปรดสนับสนุนคำตอบของคุณด้วย " ข้อเท็จจริงการอ้างอิงหรือความเชี่ยวชาญเฉพาะด้าน "


6
ฉันต้องบอกว่าการใช้การออกแบบแบบซิงเกิลทำให้ฉันรู้สึกไม่ดีเมื่อเร็ว ๆ นี้เนื่องจากฉันพยายามปรับเปลี่ยนรหัส ขณะที่ฉันทำในเวลาว่างฉันเกือบขี้เกียจเกินกว่าจะสร้างสิ่งนี้ ข่าวร้ายสำหรับผลผลิต
Marcin

71
มีคำตอบ 'ข้อเสีย' มากมาย แต่ฉันก็อยากจะเห็นตัวอย่างที่ดีของเมื่อแบบแผนนั้นดีเมื่อเปรียบเทียบกับสิ่งที่ไม่ดี ...
DGM

50
ฉันเขียนโพสต์บล็อกเกี่ยวกับเรื่องนี้เมื่อไม่กี่เดือนที่ผ่านมา: jalf.dk/blog/2010/03/… - และขอพูดเพียงนิดหน่อย ฉันไม่สามารถนึกถึงสถานการณ์เดียวที่บุคคลเดี่ยวเป็นทางออกที่ถูกต้องได้ นั่นไม่ได้หมายความว่าสถานการณ์ดังกล่าวไม่มีอยู่จริง แต่ ... การเรียกพวกเขาว่าหายากนั้นเป็นการพูดที่น้อย
jalf

8
@ อดัมมันไม่ได้หมายความว่าคุณต้องทำ แต่มันหมายความว่าคุณสามารถเข้าถึงมันได้ และถ้าคุณไม่ต้องการเข้าถึงมันอย่างนั้นก็มีเหตุผลเล็กน้อยที่จะทำให้มันกลายเป็นซิงเกิลแรก ดังนั้นการโต้แย้งของคุณจึงมีประสิทธิภาพ "ไม่มีอันตรายใด ๆ ในการสร้างซิงเกิลตันถ้าเราไม่ถือว่ามันเป็นซิงเกิลใช่เลยเยี่ยมมากรถของฉันก็ไม่สร้างมลภาวะถ้าฉันไม่ขับมัน ไม่ได้รับรถในสถานที่แรก;) (เปิดเผยเต็ม: ฉันไม่จริงมีรถ).
jalf

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

คำตอบ:


1293

ถอดความจากปุ่ม Brian:

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

  2. พวกเขาละเมิดหลักการความรับผิดชอบเดียว : โดยอาศัยอำนาจตามความจริงที่ว่าพวกเขาควบคุมการสร้างและวงจรชีวิตของตัวเอง

  3. พวกเขาโดยเนื้อแท้ทำให้เกิดรหัสที่จะแน่นคู่ สิ่งนี้ทำให้การแกล้งพวกเขาออกไปทดสอบค่อนข้างยากในหลายกรณี

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


324
ฉันไม่เห็นด้วยกับคุณ. เนื่องจากความคิดเห็นได้รับอนุญาตเพียง 600 ตัวอักษรฉันได้เขียนบล็อกโพสต์เพื่อแสดงความคิดเห็นเกี่ยวกับเรื่องนี้โปรดดูลิงค์ด้านล่าง jorudolph.wordpress.com/2009/11/22/singleton-considerations
Johannes Rudolph

61
ในจุดที่ 1 และ 4 ฉันคิดว่าซิงเกิลตันมีประโยชน์และอันที่จริงแล้วเกือบจะสมบูรณ์แบบสำหรับการแคชข้อมูล (โดยเฉพาะจาก DB) การเพิ่มประสิทธิภาพนั้นไกลกว่าความซับซ้อนที่เกี่ยวข้องในการสร้างแบบจำลองการทดสอบหน่วย
Dai Bok

56
@Dai บก "แคชข้อมูล (โดยเฉพาะอย่างยิ่งจาก DB)" ใช้พร็อกซีรูปแบบการทำเช่นนี้ ...
paxos1977

55
ว้าวคำตอบที่ดี ฉันอาจใช้ถ้อยคำก้าวร้าวมากเกินไปที่นี่ดังนั้นโปรดจำไว้ว่าฉันตอบคำถามในเชิงลบ คำตอบของฉันคือรายการสั้น ๆ ของ 'รูปแบบการต่อต้าน' ที่เกิดขึ้นจากการใช้งานซิงเกิลที่ไม่ดี การเปิดเผยแบบเต็มรูปแบบ ฉันก็ใช้ซิงเกิลตันเป็นครั้งคราวเช่นกัน มีคำถามที่เป็นกลางมากขึ้นเกี่ยวกับ SO ที่จะทำให้ฟอรัมที่ยอดเยี่ยมสำหรับเมื่อ Singletons ถือเป็นความคิดที่ดี ตัวอย่างเช่นstackoverflow.com/questions/228164/…
Jim Burger

21
พวกเขาไม่ได้ยอดเยี่ยมสำหรับการแคชในสภาพแวดล้อมแบบมัลติเธรด คุณสามารถเอาชนะแคชได้อย่างง่ายดายด้วยการต่อสู้หลายเธรดในทรัพยากรที่ จำกัด [ดูแลโดยซิงเกิล]
monksy

449

Singletons แก้ปัญหาหนึ่ง (และเพียงหนึ่ง)

การช่วงชิงทรัพยากร

หากคุณมีทรัพยากรบางอย่างที่

( 1 ) มีได้เพียงครั้งเดียวเท่านั้นและ

( 2 ) คุณต้องจัดการอินสแตนซ์เดียวนั้น

คุณจำเป็นต้องมีเดี่ยว

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

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

เมื่อคุณคิดว่าคุณต้องการโลกคุณอาจจะทำผิดพลาดอย่างมากในการออกแบบ


43
ฮาร์ดแวร์เป็นตัวอย่างใช่มั้ย ระบบสมองกลฝังตัวมีฮาร์ดแวร์มากมายที่อาจใช้ซิงเกิลตันหรืออาจเป็นฮาร์ดแวร์ตัวใหญ่? <grin>
Jeff

170
เห็นด้วยอย่างสิ้นเชิง. มีการกำหนดจำนวนมาก "การปฏิบัตินี้ไม่ดี" พูดคุยลอยไปรอบ ๆ โดยไม่ได้รับการยอมรับว่าการปฏิบัติอาจมีสถานที่ บ่อยครั้งที่การฝึกนั้นเป็นเพียง "ไม่ดี" เนื่องจากมีการใช้งานในทางที่ผิดบ่อยครั้ง ไม่มีอะไรผิดปกติกับรูปแบบซิงเกิลตราบใดที่มันถูกนำไปใช้อย่างเหมาะสม
Damovisa

53
จริงโดยหลักการซิงเกิลตันนั้นหายากมาก (และคิวเครื่องพิมพ์ไม่แน่นอนและไม่ใช่ไฟล์บันทึก - ดู log4j) บ่อยครั้งกว่าฮาร์ดแวร์ไม่ได้เกิดจากความบังเอิญมากกว่าที่จะเป็นหลักการ ฮาร์ดแวร์ทั้งหมดที่เชื่อมต่อกับพีซีนั้นดีที่สุดคือซิงเกิลบังเอิญ (คิดว่าจอภาพหลายจอ, เครื่องพิมพ์, การ์ดเสียง) แม้แต่เครื่องตรวจจับอนุภาคขนาด 500 mio $ เป็นเครื่องตรวจจับความบังเอิญและข้อ จำกัด ด้านงบประมาณซึ่งไม่สามารถใช้กับซอฟต์แวร์ได้ดังนั้นจึงไม่มีเครื่องตรวจจับเดี่ยว ในการสื่อสารโทรคมนาคมหมายเลขโทรศัพท์หรือโทรศัพท์จริงไม่ใช่แบบซิงเกิล (คิดว่า ISDN, call center)
digitalarbeiter

23
ฮาร์ดแวร์อาจมีทรัพยากรได้หลายอินสแตนซ์เดียว แต่ฮาร์ดแวร์ไม่ใช่ซอฟต์แวร์ ไม่มีเหตุผลที่พอร์ตอนุกรมเดียวบนบอร์ดพัฒนาควรเป็นแบบจำลองเป็นซิงเกิล แน่นอนการสร้างแบบจำลองนั้นจะทำให้ยากต่อการพอร์ตไปยังบอร์ดใหม่ที่มีสองพอร์ต!
dash-tom-bang

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

352

Snobs ที่เข้ารหัสบางคนมองดูพวกเขาในฐานะที่เป็นเพียงผู้มีชื่อเสียงระดับโลก ในลักษณะเดียวกับที่หลาย ๆ คนเกลียดข้ามคำสั่งมีคนอื่น ๆ ที่เกลียดชังความคิดของที่เคยใช้ที่ทั่วโลก ฉันได้เห็นนักพัฒนาหลายคนไปที่ความยาวพิเศษเพื่อหลีกเลี่ยงระดับโลกเพราะพวกเขาพิจารณาใช้เป็นข้อผิดพลาด แปลก แต่จริง

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


17
ความล้มเหลวที่ฉันเห็นใน Singleton คือคนใช้แทน globals เพราะอย่างใด "ดีกว่า" ปัญหาคือ (เท่าที่ฉันเห็น) ว่าสิ่งที่ซิงเกิลนำมาสู่ตารางในกรณีเหล่านี้ไม่เกี่ยวข้อง (การสร้างแบบต่อการใช้งานครั้งแรกนั้นเป็นเรื่องเล็กน้อยที่จะนำไปใช้ใน non-singleton เช่นแม้ว่ามันจะไม่ได้ใช้ตัวสร้างที่จะทำ)
dash-tom-bang

21
เหตุผลที่ "เรา" ดูถูกพวกเขาก็คือ "เรา" บ่อยครั้งที่พวกเขาเห็นว่าใช้ผิดและบ่อยเกินไป "เรา" รู้หรือไม่ว่าพวกเขามีต้นเหตุ
Bjarke Freund-Hansen

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

22
@Pacerier เมื่อใดก็ตามที่มีเงื่อนไขดังต่อไปนี้: (1) คุณต้องการเพียงบางสิ่งเท่านั้น (2) คุณต้องผ่านสิ่งนั้นเป็นอาร์กิวเมนต์ในการเรียกใช้เมธอดจำนวนมาก (3) คุณยินดีที่จะยอมรับ โอกาสที่จะต้อง refactor สักวันหนึ่งเพื่อแลกกับการลดขนาดและความซับซ้อนของรหัสของคุณโดยทันทีโดยไม่ผ่านสิ่งที่น่ากลัวไปทุกที่
antinome

18
ฉันไม่รู้สึกว่าสิ่งนี้ตอบอะไรมันแค่บอกว่า 'บางครั้งมันอาจพอดีบางครั้งก็อาจไม่' ตกลง แต่ทำไมและเมื่อไร อะไรทำให้คำตอบนี้มากกว่าการโต้แย้งเพื่อการกลั่นกรอง ?
Guildenstern

219

Misko Hevery จาก Google มีบทความที่น่าสนใจในหัวข้อนี้ ...

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

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

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


8
บทความของ Misko ในหัวข้อเป็นสิ่งที่ดีที่สุดที่เกิดขึ้นในขณะนี้
Chris Mayer

22
ลิงค์แรกไม่ได้แก้ปัญหากับซิงเกิลตัน แต่สมมติว่ามีการพึ่งพาแบบคงที่ภายในคลาส เป็นไปได้ที่จะแก้ไขตัวอย่างที่กำหนดโดยส่งผ่านพารามิเตอร์ แต่ยังคงใช้ singletons
DGM

17
@DGM: แน่นอน - ในความเป็นจริงมีการตัดการเชื่อมต่อตรรกะขนาดใหญ่ระหว่างส่วน 'เหตุผล' ของบทความและส่วน 'Singletons เป็นสาเหตุ'
Harper Shelby

1
บทความ CreditCard Misko ของเป็นมากตัวอย่างของการใช้ผิดวัตถุประสงค์ของรูปแบบนี้
อเล็กซ์

2
การอ่านบทความที่สอง Singletons เป็นส่วนหนึ่งของแบบจำลองวัตถุที่อธิบายไว้ อย่างไรก็ตามแทนที่จะสามารถเข้าถึงได้ทั่วโลกสิ่งเหล่านี้จะเป็นส่วนตัวกับวัตถุของโรงงาน
FruitBreak

121

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

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


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

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

4
@Dainius: ในที่สุดก็ไม่มีจริง ๆ แน่นอนว่าคุณจะไม่ได้รับการแทนที่ด้วยอินสแตนซ์อื่นโดยพลการ (ยกเว้นช่วงเวลาที่ทำให้คุณต้องเผชิญหน้ากับเรื่องใบหน้าฉันเคยเห็นsetInstanceวิธีการมาก่อนแล้ว) แม้ว่ามันจะไม่ค่อยสำคัญเท่าไร - weenie ที่ "ต้องการ" ซิงเกิลตันก็ไม่ทราบว่ามีเรื่องประหลาดเกี่ยวกับการห่อหุ้มหรือสิ่งผิดปกติ รัฐทั่วโลกที่ไม่แน่นอนดังนั้นเขาจึงช่วยเหลือผู้ตั้งถิ่นฐานทุกคน (?) เดียว สนาม (และใช่สิ่งนี้เกิดขึ้นมากเกือบทุกซิงเกิลที่ฉันเคยเห็นในป่านั้นไม่แน่นอนโดยการออกแบบและมักจะน่าอายเช่นนั้น)
cHao

4
@Dainius: ในระดับใหญ่เรามีอยู่แล้ว "ชอบการเรียบเรียงเสียงประสานมากกว่าการสืบทอด" เป็นเรื่องที่ค่อนข้างมานานแล้ว เมื่อการสืบทอดเป็นวิธีแก้ปัญหาที่ดีที่สุดคุณก็สามารถใช้งานได้อย่างอิสระ สิ่งเดียวกันกับ singletons, globals, เธรดและgotoอื่น ๆ พวกเขาอาจทำงานในหลายกรณี แต่ตรงไปตรงมา "ทำงาน" ไม่เพียงพอ - ถ้าคุณต้องการที่จะต่อต้านภูมิปัญญาดั้งเดิมคุณควรแสดงให้เห็นว่าวิธีการของคุณเป็นอย่างไร เป็นที่ดีขึ้นกว่าการแก้ปัญหาการชุมนุม และฉันยังไม่เห็นกรณีดังกล่าวสำหรับรูปแบบซิงเกิล
cHao

3
เกรงว่าเราจะคุยกันฉันไม่ได้พูดถึงตัวอย่างที่มีอยู่ทั่วโลก มีหลายกรณีที่ สิ่งที่ฉันกำลังพูดถึง (โดยเฉพาะอย่างยิ่งเมื่อฉันพูดว่า "capital-S Singleton") เป็นรูปแบบ Singleton ของ GoF ซึ่งฝังอินสแตนซ์เดียวทั่วโลกในชั้นเรียนด้วยตัวมันเองเปิดเผยด้วยวิธีการgetInstanceหรือชื่อที่คล้ายกันและป้องกันการดำรงอยู่ของ ตัวอย่างที่สอง ตรงไปตรงมาที่จุดที่คุณอาจรวมทั้งไม่ได้มีอย่างใดอย่างหนึ่งเช่น
cHao

72

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

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

ดังนั้นโดยทั่วไป:

public MyConstructor(Singleton singleton) {
    this.singleton = singleton;
}

ค่อนข้างมากกว่า:

public MyConstructor() {
    this.singleton = Singleton.getInstance();
}

ฉันเชื่อว่ารูปแบบนี้เรียกว่าการฉีดแบบพึ่งพาและโดยทั่วไปถือว่าดี

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


17
เฮ้ถ้าคุณทำสิ่งนี้ทุกที่แล้วคุณจะต้องผ่านการอ้างอิงไปยัง Singelton ทุกที่และทำให้คุณไม่มีซิงเกิลอีกต่อไป :) (ซึ่งมักเป็นสิ่งที่ดี IMO)
Bjarke Freund-Hansen

2
@ BjarkeFreund-Hansen - ไร้สาระคุณกำลังพูดถึงอะไร ซิงเกิลเป็นเพียงตัวอย่างของคลาสที่ไม่มีการเริ่มต้นอีกครั้ง การอ้างอิงเช่น Singleton ไม่ได้คัดลอกวัตถุจริงมันแค่อ้างอิงมัน - คุณยังมีวัตถุเดียวกัน (อ่าน: Singleton)
M. Mimpen

2
@ M.Mimpen: ไม่ทุน -S Singleton (ซึ่งเป็นสิ่งที่ถูกกล่าวถึงที่นี่) เป็นตัวอย่างของคลาสที่ (a) รับประกันว่าจะมีเพียงหนึ่งอินสแตนซ์เท่านั้นที่เคยมีอยู่และ (b) เข้าถึงได้ผ่านคลาสของตัวเอง จุดเข้าถึงสากลในตัว หากคุณได้ประกาศว่าไม่มีสิ่งใดที่ควรจะเรียกgetInstance()(ข) ก็ไม่เป็นความจริงอีกต่อไป
cHao

2
@cHao ฉันไม่ได้ติดตามคุณหรือคุณไม่เข้าใจผู้ที่ฉันแสดงความคิดเห็น - ซึ่งคือ Bjarke Freund-Hansen Bjarke ระบุว่าการอ้างอิงผลซิงเกิลหลายครั้งมีซิงเกิลหลายอัน แน่นอนว่าไม่เป็นความจริงเพราะไม่มีสำเนาลึก
M. Mimpen

6
@ M.Mimpen: ฉันใช้ความคิดเห็นของเขาเพื่ออ้างถึงผลความหมาย เมื่อคุณเรียกคนผิดกฎหมายมาgetInstance()คุณจะต้องโยนความแตกต่างที่เป็นประโยชน์อย่างหนึ่งระหว่างรูปแบบซิงเกิลและการอ้างอิงธรรมดา ตราบเท่าที่ส่วนที่เหลือของรหัสเกี่ยวข้องความเป็นโสดไม่ใช่ทรัพย์สินอีกต่อไป เฉพาะผู้โทรที่getInstance()เคยต้องการรู้หรือสนใจว่ามีกี่ครั้ง ด้วยผู้โทรเพียงคนเดียวค่าใช้จ่ายในความพยายามและความยืดหยุ่นสำหรับชั้นเรียนในการบังคับใช้ความเป็นโสดได้อย่างน่าเชื่อถือมากกว่าการมีผู้โทรเพียงเก็บการอ้างอิงและนำมาใช้ซ้ำ
cHao

69

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

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

ในสภาพแวดล้อมที่เก็บขยะ singletons สามารถกลายเป็นปัญหาเกี่ยวกับการจัดการหน่วยความจำได้อย่างรวดเร็ว

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


4
ฉันรู้ว่ามันเป็นด้ายอายุหลายปี hi @Kimoz u กล่าวว่า: - ซิงเกิลตันสามารถกลายเป็นปัญหาเกี่ยวกับการจัดการหน่วยความจำได้อย่างรวดเร็ว ต้องการอธิบายรายละเอียดเพิ่มเติมว่าปัญหาใดที่อาจเกิดขึ้นกับการรวบรวมซิงเกิลและขยะ
โทมัส

@ Kimoz คำถามที่ถามว่า"ทำไมรูปแบบซิงเกิลจึงไม่มีปัญหาในตัวเอง" และคุณได้ทำซ้ำจุด แต่ไม่ให้แม้แต่กรณีการใช้งานที่ถูกต้องของรูปแบบซิงเกิล
Pacerier

@Thomas เนื่องจากมี singleton by definition อยู่ในอินสแตนซ์เดียวเท่านั้น ดังนั้นจึงมักจะซับซ้อนในการกำหนดการอ้างอิงถึง null เท่านั้น สามารถทำได้ แต่หมายความว่าคุณสามารถควบคุมจุดหลังจากที่ไม่ได้ใช้ซิงเกิลในแอพของคุณ และนี่เป็นของหายากและมักจะตรงข้ามกับสิ่งที่ผู้ชื่นชอบเดี่ยวต้องการ: วิธีง่ายๆในการทำให้สามารถเข้าถึงตัวอย่างได้ตลอดเวลา ในกรอบการทำงาน DI บางอย่างเช่น Guice หรือ Dagger จะไม่สามารถกำจัดซิงเกิลตันและอยู่ในความทรงจำตลอดไป (แม้ว่าภาชนะที่จัดเตรียมไว้จะดีกว่าแบบทำที่บ้าน)
Snicolas

54

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


9
นั่นฟังดูคล้ายกับปัญหาของการทดสอบหน่วยที่สามารถทดสอบวัตถุ (หน่วย), ฟังก์ชั่น (หน่วย), ไลบรารีทั้งหมด (หน่วย), แต่ล้มเหลวกับสิ่งใดที่คงที่ในคลาส (เช่นหน่วย)
v010dya

2
คุณไม่คิดว่าจะทำการอ้างอิงภายนอกทั้งหมดหรือไม่? หากคุณเป็นเช่นนั้นแล้วปัญหาที่เกิดขึ้นกับ moc singleton คือถ้าไม่คุณกำลังทำการทดสอบหน่วยใช่หรือไม่
Dainius

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

Powermock สามารถเยาะเย้ยสิ่งคงที่
Snicolas

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

45

Singletons นอกจากนี้ยังมีที่ไม่ดีเมื่อมันมาถึงการจัดกลุ่ม เพราะตอนนี้คุณไม่มี "ใบเดียวอย่างเดียว" ในใบสมัครของคุณอีกต่อไป

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

public class SingletonDao {
    // songleton's static variable and getInstance() method etc. omitted
    public void writeXYZ(...){
        synchronized(...){
            // some database writing operations...
        }
    }
}

SingletonDaoดังนั้นคุณจะแน่ใจว่ามีเพียงหนึ่งเดี่ยวในใบสมัครของคุณอยู่และฐานข้อมูลทั้งหมดไปผ่านคนนี้และมีเพียง สภาพแวดล้อมการผลิตของคุณตอนนี้มีลักษณะดังนี้: ซิงเกิลซิงเกิล

ทุกอย่างเรียบร้อยดี

ตอนนี้ให้พิจารณาว่าคุณต้องการตั้งค่าอินสแตนซ์ของเว็บแอปพลิเคชันหลายอินสแตนซ์ในคลัสเตอร์ ตอนนี้คุณก็มีอะไรแบบนี้:

หลายซิงเกิล

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

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


4
หากคุณไม่ทราบวิธีการใช้งานซิงเกิลตันคุณไม่ควรทำเช่นนั้น หากคุณไม่รู้ว่าคุณกำลังทำอะไรคุณควรหาสิ่งนั้นก่อนและหลังจากนั้นก็ทำสิ่งที่คุณต้องการ
Dainius

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

39
  1. มันใช้งานง่าย (ab) เป็นตัวแปรทั่วโลก
  2. คลาสที่ขึ้นอยู่กับ singletons นั้นค่อนข้างยากในการทดสอบแบบแยกหน่วย

33

การผูกขาดเป็นปีศาจและซิงเกิลที่มีสถานะที่ไม่สามารถอ่านได้ / ไม่แน่นอนเป็นปัญหาที่เกิดขึ้นจริง ...

หลังจากอ่านSingletons เป็น Liars ทางพยาธิวิทยาตามคำแนะนำในคำตอบของ jasonฉันได้พบชิ้นอาหารอันโอชะเล็ก ๆ น้อย ๆ นี้ซึ่งเป็นตัวอย่างที่ดีที่สุดในการนำเสนอว่า singletons ถูกใช้ในทางที่ผิดอย่างไร

โลกไม่ดีเพราะ:

  • มันทำให้เกิดความขัดแย้ง namespace
  • ข มันเผยให้เห็นรัฐในแบบที่ไม่รับประกัน

เมื่อพูดถึง Singletons

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

ในคำสั่งสุดท้ายเขาอ้างถึงแนวคิดของบล็อกของ 'ซิงเกิลตันเป็นคนโกหก'

สิ่งนี้นำไปใช้กับการผูกขาดได้อย่างไร

ในการเริ่มเกมผูกขาด:

  • เราสร้างกฎก่อนเพื่อให้ทุกคนอยู่ในหน้าเดียวกัน
  • ทุกคนได้รับการเริ่มต้นที่เท่าเทียมกันตั้งแต่เริ่มเกม
  • มีการนำเสนอกฎชุดเดียวเท่านั้นเพื่อหลีกเลี่ยงความสับสน
  • กฎไม่ได้รับอนุญาตให้เปลี่ยนตลอดทั้งเกม

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

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

ตอนนี้เกมเริ่มต้นจากการทอยลูกเต๋าอย่างเป็นมิตรกับธุรกิจที่จริงจัง บ๊อบเป็นตัวอย่างของความล้มเหลวและโจและเอ็ดไม่ต้องการที่จะจบลงเช่น 'ผู้ชายคนนั้น' ดังนั้นการเป็นผู้เล่นชั้นนำของคุณในทันทีกลายเป็นศัตรู Joe และ Ed เริ่มฝึกฝนการเทรดภายใต้โต๊ะหลังการอัดฉีดเงินการแลกเปลี่ยนบ้านโดยไม่คำนึงถึงคุณค่าและโดยทั่วไปทุกอย่างจะทำให้คุณอ่อนแอในฐานะผู้เล่นจนกว่าจะมีผู้เล่นคนใดคนหนึ่งขึ้นไปด้านบน

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

ดังนั้นหากกฎของเกมแสดงถึงซิงเกิลตันอย่างถูกต้องกฎการผูกขาดจะเป็นตัวอย่างของการละเมิด

สิ่งนี้นำไปใช้กับการเขียนโปรแกรมได้อย่างไร

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

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

Singleton เป็นเพียงตัวเลือกหากคุณต้องการสิ่งที่ Singleton มอบให้ อินสแตนซ์อ่านอย่างเดียวแบบเขียนของวัตถุ กฎเดียวกันนั้นควรเรียงซ้อนกับคุณสมบัติ / สมาชิกของวัตถุเช่นกัน


1
เกิดอะไรขึ้นถ้าซิงเกิลตันเก็บข้อมูลบางอย่าง?
Yola

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

@EvanPlaice: Singleton.getInstance()แม้จะมีการจำลองคุณต้องการมีบางประเด็นที่มีรหัสที่ระบุว่า ภาษาที่รองรับการสะท้อนอาจสามารถแก้ไขได้โดยการตั้งค่าฟิลด์ที่จัดเก็บอินสแตนซ์หนึ่งที่แท้จริง แม้ว่าการทดสอบ IMO จะมีความน่าเชื่อถือน้อยลงเล็กน้อยเมื่อคุณเข้าร่วมการทดสอบด้วยสถานะส่วนตัวของชั้นเรียนอื่น
cHao

28

ซิงเกิลไม่ได้เกี่ยวกับอินสแตนซ์เดียว!

ต่างจากคำตอบอื่น ๆ ฉันไม่ต้องการพูดถึงสิ่งที่ผิดพลาดกับ Singletons แต่เพื่อแสดงให้คุณเห็นว่ามันมีประสิทธิภาพและยอดเยี่ยมมากแค่ไหนเมื่อใช้ถูกต้อง!

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

คุณสามารถแมปMyModelไปยังTestMyModelคลาสที่สืบทอดมันได้ทุกที่เมื่อMyModelจะถูกฉีดคุณจะได้รับTestMyModelinstread - ปัญหา : ซิงเกิลตันอาจทำให้หน่วยความจำรั่วเนื่องจากไม่เคยทิ้ง
วิธีแก้ปัญหา : เอาล่ะทิ้งไป! ใช้การติดต่อกลับในแอปของคุณเพื่อกำจัดซิงเกิลตันอย่างถูกต้องคุณควรลบข้อมูลใด ๆ ที่เชื่อมโยงกับพวกเขาและในที่สุด: ลบออกจากโรงงาน

อย่างที่ฉันบอกไว้ในหัวข้อซิงเกิลนั้นไม่เกี่ยวกับอินสแตนซ์เดียว

  • ช่วยเพิ่มความสามารถในการอ่าน : คุณสามารถดูชั้นเรียนของคุณและดูว่ามีการฉีดเข้าไปในรูปแบบใดเพื่อดูว่ามันขึ้นต่อกันอย่างไร
  • ปรับปรุงการบำรุงรักษา : เมื่อคุณลบการพึ่งพาจากคลาสที่คุณเพิ่งลบการฉีดซิงเกิลคุณไม่จำเป็นต้องไปและแก้ไขลิงค์ใหญ่ของคลาสอื่น ๆ ที่เพิ่งย้ายการอ้างอิงของคุณไปรอบ ๆ (นี่คือรหัสส่งกลิ่นสำหรับฉัน@Jim Burger )
  • Singletons ปรับปรุงหน่วยความจำและประสิทธิภาพการทำงาน : เมื่อมีบางอย่างเกิดขึ้นในแอปพลิเคชันของคุณและใช้เวลานานในการโทรกลับเพื่อส่งมอบคุณกำลังสูญเสียความทรงจำและประสิทธิภาพโดยใช้ Singleton ที่คุณกำลังตัดชายกลางและปรับปรุงประสิทธิภาพและการใช้หน่วยความจำ โดยหลีกเลี่ยงการจัดสรรตัวแปรท้องถิ่นที่ไม่จำเป็น)

2
สิ่งนี้ไม่ได้แก้ไขปัญหาหลักของฉันกับ Singletons ซึ่งอนุญาตให้เข้าถึงสถานะส่วนกลางจากคลาสใด ๆ นับพันในโครงการของคุณ
LegendLength

8
นั่นจะเป็นจุดประสงค์ ...
Ilya Gazman

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

23

คำตอบของฉันเกี่ยวกับวิธีการที่ซิงเกิ้ลไม่ดีอยู่เสมอ "พวกเขายากที่จะทำถูกต้อง" องค์ประกอบพื้นฐานหลายภาษาเป็นแบบซิงเกิล (คลาส, ฟังก์ชัน, เนมสเปซและแม้กระทั่งตัวดำเนินการ) เช่นเดียวกับส่วนประกอบในด้านอื่น ๆ ของการคำนวณ (localhost, เส้นทางเริ่มต้น, ระบบไฟล์เสมือน ฯลฯ ) และไม่ได้ตั้งใจ ในขณะที่พวกเขาทำให้เกิดปัญหาและความยุ่งยากในบางครั้งพวกเขาก็สามารถทำให้หลายสิ่งหลายอย่างทำงานได้ดีขึ้นมาก

สกรูใหญ่สองอันที่ฉันเห็นคือการปฏิบัติเหมือนโลกและไม่สามารถกำหนดการปิดแบบซิงเกิลได้

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

บริบทของซิงเกิลก็สำคัญเช่นกัน ลักษณะการกำหนดของ Singleton คือมี "เพียงหนึ่งเดียว" แต่ความจริงก็คือ "เพียงหนึ่งเดียว" ในบริบท / เนมสเปซบางชนิด พวกเขามักจะเป็นหนึ่งใน: หนึ่งต่อเธรดกระบวนการที่อยู่ IP หรือคลัสเตอร์ แต่ยังสามารถเป็นหนึ่งต่อหน่วยประมวลผล, เครื่อง, ภาษา namespace / class loader / อะไรก็ตาม, subnet, อินเทอร์เน็ต, ฯลฯ

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

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

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


23

ดู Wikipedia Singleton_pattern

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

การอ้างอิง (การอ้างอิงที่เกี่ยวข้องเท่านั้นจากบทความ)

  1. ^ Alex Miller รูปแบบที่ฉันเกลียด # 1: ซิงเกิลตัน , กรกฎาคม 2550
  2. ^ Scott Densmore เหตุใดซิงเกิลจึงชั่วร้ายพฤษภาคม 2004
  3. ^ Steve Yegge Singletons ถือว่าโง่กันยายน 2004
  4. ^ JB Rainsberger, IBM ใช้ซิงเกิลตันของคุณอย่างฉลาดกรกฎาคม 2544

8
คำอธิบายเกี่ยวกับรูปแบบที่ไม่ได้อธิบายว่าทำไมจึงถือว่าเป็นความชั่วร้าย ...
Jrgns

2
ไม่ยุติธรรม: "มันยังถือว่าเป็นรูปแบบการต่อต้านโดยบางคนที่รู้สึกว่ามันถูกใช้มากเกินไปแนะนำข้อ จำกัด ที่ไม่จำเป็นในสถานการณ์ที่อินสแตนซ์ชั้นเดียวไม่จำเป็นต้องใช้จริง" ดูการอ้างอิง ... ยังไงก็ตามฉันมี RTFM
GUI Junkie

17

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

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

  1. ตรวจสอบให้แน่ใจว่าคลาสที่จะใช้เป็น singleton ใช้อินเทอร์เฟซ สิ่งนี้อนุญาตให้สตับหรือ mocks ใช้งานได้โดยใช้อินเตอร์เฟสเดียวกัน

  2. ตรวจสอบให้แน่ใจว่า Singleton ปลอดภัยต่อเธรด นั่นคือที่ได้รับ

  3. ซิงเกิลควรเรียบง่ายในธรรมชาติและไม่ซับซ้อนเกินไป

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

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

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


+1 ในที่สุดคำตอบที่กล่าวถึงเมื่อซิงเกิลอาจใช้ได้
Pacerier

16

ฉันต้องการพูดถึง 4 คะแนนในคำตอบที่ได้รับการยอมรับหวังว่าบางคนสามารถอธิบายได้ว่าทำไมฉันถึงผิด

  1. ทำไมการซ่อนรหัสอ้างอิงของคุณจึงไม่ดี มีการอ้างอิงที่ซ่อนอยู่หลายสิบรายการ (การโทรแบบรันไทม์ C, การโทรแบบ OS API, การเรียกใช้ทั่วโลก) และการอ้างอิงแบบซิงเกิลหาง่าย (ค้นหาตัวอย่าง ())

    "การสร้างบางสิ่งที่ทั่วโลกเพื่อหลีกเลี่ยงการส่งผ่านเป็นกลิ่นรหัส" ทำไมไม่ผ่านบางสิ่งบางอย่างเพื่อหลีกเลี่ยงการทำให้เป็นกลิ่นรหัสเดียว?

    หากคุณกำลังส่งวัตถุผ่าน 10 ฟังก์ชั่นใน call stack เพียงเพื่อหลีกเลี่ยงซิงเกิลตันมันยอดเยี่ยมมากเหรอ?

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

  3. เหตุใดการส่งวัตถุไปยังคลาสทำให้คู่นั้นแน่นกว่าการใช้วัตถุนั้นเป็นซิงเกิลจากภายในคลาส?

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

เกี่ยวกับการทดสอบหน่วย:

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

1
1. การพึ่งพาที่ซ่อนอยู่ทั้งหมดเหล่านั้น? สิ่งเหล่านั้นก็ไม่ดีเช่นกัน การพึ่งพาที่ซ่อนอยู่นั้นเป็นสิ่งชั่วร้ายเสมอ แต่ในกรณีของ CRT และ OS พวกเขามีอยู่แล้วและเป็นวิธีเดียวที่จะทำให้เสร็จ (ลองเขียนโปรแกรม C โดยไม่ต้องใช้ runtime หรือ OS) ความสามารถในการทำสิ่งต่าง ๆ นั้นมีค่ามากกว่าเชิงลบ ซิงเกิลในรหัสของเราไม่ได้รับความหรูหรานั้น เนื่องจากมันอยู่ในขอบเขตการควบคุมและความรับผิดชอบของเราอย่างแน่นหนาการใช้งานทุกอย่าง (อ่าน: การพึ่งพาที่ซ่อนอยู่เพิ่มเติม) ควรมีเหตุผลว่าเป็นวิธีการที่สมเหตุสมผลในการทำสิ่งต่างๆ มีน้อยมากจริงๆ
cHao

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

1
@cHao: 1. เป็นเรื่องดีที่คุณยอมรับว่า "ความสามารถในการทำสิ่งต่าง ๆ มีค่ามากกว่าเชิงลบ" ตัวอย่างจะเป็นกรอบการบันทึก มันเป็นความชั่วร้ายที่จะสั่งให้ส่งผ่านวัตถุบันทึกทั่วโลกโดยการฉีดพึ่งพาผ่านชั้นของการเรียกใช้ฟังก์ชั่นถ้ามันหมายความว่านักพัฒนาจะท้อจากการบันทึก ดังนั้นซิงเกิล 3. จุดเชื่อมต่อที่แน่นหนาของคุณมีความเกี่ยวข้องถ้าคุณต้องการให้ลูกค้าในชั้นเรียนควบคุมพฤติกรรมผ่านความหลากหลาย มักจะไม่เป็นเช่นนั้น
Jon

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

1
3. ถ้าคุณไม่ต้องการโพลีมอร์ฟิซึมคุณไม่จำเป็นต้องมีตัวอย่าง คุณอาจทำให้คลาสเป็นแบบสแตติกทำให้คุณผูกตัวเองกับวัตถุหนึ่ง ๆ และใช้งานเพียงครั้งเดียว - และอย่างน้อย API ก็ไม่ได้โกหกคุณมากนัก
cHao

15

Vince Hustonมีเกณฑ์เหล่านี้ซึ่งดูสมเหตุสมผลสำหรับฉัน:

ควรพิจารณาซิงเกิลตันเฉพาะเมื่อตรงตามเกณฑ์ทั้งสามข้อต่อไปนี้:

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

หากความเป็นเจ้าของอินสแตนซ์เดียวเวลาและวิธีการเริ่มต้นเกิดขึ้นและการเข้าถึงทั่วโลกไม่เป็นปัญหา Singleton ไม่น่าสนใจเพียงพอ


14

Singletons ไม่ดีจากมุมมองของคนเจ้าระเบียบ

จากจุด pratical ในมุมมองของเดี่ยวคือการปิดการพัฒนาเวลา VS ซับซ้อน

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

Singletons บางครั้งยังซับซ้อนทดสอบหน่วย


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

1
AFAIK Singleton เป็นวิธีการที่โรงงาน ดังนั้นฉันไม่ได้รับคำแนะนำของคุณ
tacone

3
"A factory"! = "A factory_method ที่ไม่สามารถแยกออกจากสิ่งที่มีประโยชน์อื่น ๆ ในระดับเดียวกัน"
Sven

13

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

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


6
ฉันคิดว่าแบคแลชกลับมีส่วนเกี่ยวข้องกับผู้ที่ฝึกฝนการทดสอบหน่วยอย่างเป็นทางการมากขึ้นโดยตระหนักว่าพวกเขาเป็นฝันร้ายที่ต้องจัดการกับมัน
Jim Burger

1
ไม่แน่ใจว่าทำไมถึงอยู่ที่ -1 มันไม่ใช่คำตอบที่สื่อความหมายมากที่สุด แต่เขาก็ไม่ผิดเหมือนกัน
Outlaw Programmer

13

ฉันจะไม่แสดงความคิดเห็นในการโต้แย้งที่ดี / ชั่วร้าย แต่ฉันไม่ได้ใช้พวกเขาตั้งแต่ฤดูใบไม้ผลิมาพร้อม การใช้การฉีดแบบพึ่งพานั้นได้ลบความต้องการของฉันไปแล้วสำหรับ singleton, servicelocators และโรงงาน ฉันพบว่าสภาพแวดล้อมที่มีประสิทธิภาพและสะอาดกว่านี้อย่างน้อยสำหรับประเภทของงานที่ฉันทำ (เว็บแอปพลิเคชันบน Java)


3
ไม่ได้เป็นถั่วสปริงโดยค่าเริ่มต้น Singletons?
ผอม

3
ใช่ แต่ฉันหมายถึงซิงเกิล 'coding' ฉันไม่ได้เขียน 'singleton' (เช่นกับตัวสร้างส่วนตัว yada yada yada ตามรูปแบบการออกแบบ) - แต่ใช่ด้วยสปริงฉันใช้อินสแตนซ์เดียว
กฎข้อ

4
ดังนั้นเมื่อคนอื่นทำมันก็โอเค (ถ้าฉันจะใช้มันหลังจากนั้น) แต่ถ้าคนอื่นทำมันและฉันจะไม่ใช้มันมันชั่วร้าย?
Dainius

11

Singleton เป็นรูปแบบและสามารถใช้หรือใช้ในทางที่ผิดเหมือนกับเครื่องมืออื่น ๆ

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


1
ขอบคุณ Loki :) จุดของฉันแน่นอน ล่าแม่มดทั้งหมดทำให้ฉันนึกถึงการโตโต้โต้เถียง เครื่องมือสำหรับผู้ที่รู้วิธีใช้งาน การใช้เครื่องมือที่คุณไม่ชอบอาจเป็นอันตรายสำหรับคุณดังนั้นหลีกเลี่ยง แต่อย่าบอกคนอื่นว่าไม่ควรใช้ / ไม่เรียนรู้การใช้อย่างถูกต้อง ฉันไม่ได้ว่า "โปรตัน" แต่ฉันชอบความจริงที่ว่าฉันมีเครื่องมือเช่นนั้นและฉันสามารถรู้สึกว่ามันเหมาะสมที่จะใช้ ระยะเวลา
dkellner

8

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

Singletons ทำให้มันยากมากที่จะย้ายจากพวกเขาไปยังวัตถุปกติ

นอกจากนี้มันง่ายเกินไปที่จะเขียนซิงเกิลที่ไม่ปลอดภัยต่อเธรด

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

void some_class::some_function(parameters, service_provider& srv)
{
    srv.get<error_logger>().log("Hi there!");
    this->another_function(some_other_parameters, srv);
}

4
การผ่านอาร์กิวเมนต์สำหรับทุกวิธีนั้นยอดเยี่ยมควรไปอย่างน้อย 10 วิธีที่จะทำให้ API ของคุณสกปรก
Dainius

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

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

คุณสามารถให้ภาษาที่จะถ่ายทอดเป็นสายซ้อนกัน?
Dainius

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

8

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

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

FWIW ถ้าเป็น PIA เมื่อคุณลองทดสอบหน่วยมันจะไปที่ PIA เมื่อคุณพยายามที่จะทำการดีบักแก้ไขข้อผิดพลาดหรือปรับปรุง


8

บทความล่าสุดในเรื่องนี้โดยคริส Reath ที่เข้ารหัสโดยไม่มีความคิดเห็น

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

http://geekswithblogs.net/AngelEyes/archive/2013/09/08/singleton-i-love-you-but-youre-bringing-me-down-re-uploaded.aspx


7

ซิงเกิลนั้นไม่เลว มันจะไม่ดีก็ต่อเมื่อคุณทำบางสิ่งที่ไม่เหมือนใครในโลก

อย่างไรก็ตามมี "บริการขอบเขตแอปพลิเคชัน" (คิดเกี่ยวกับระบบการส่งข้อความที่ทำให้องค์ประกอบมีการโต้ตอบ) - CALLS นี้สำหรับซิงเกิลตัน "MessageQueue" - คลาสที่มีเมธอด "SendMessage (... )"

จากนั้นคุณสามารถทำสิ่งต่อไปนี้จากทั่วทุกที่:

MessageQueue.Current.SendMessage (ใหม่ MailArrivedMessage (... ));

และแน่นอนทำ:

MessageQueue.Current.RegisterReceiver (นี้);

ในคลาสที่ใช้ IMessageReceiver


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

1
ทำไมเราไม่ควรมี CustomerOrderList ทั่วโลกด้วยดังนั้นเราจึงสามารถเรียกมันได้จากทุกที่อย่างเช่น MessageQueue ฉันเชื่อว่าคำตอบนั้นเหมือนกันสำหรับทั้งคู่: มันสร้างตัวแปรระดับโลกได้อย่างมีประสิทธิภาพ
LegendLength

7

มีคนจำนวนมากใส่วัตถุที่ไม่ปลอดภัยในรูปแบบด้ายเดี่ยว ฉันได้เห็นตัวอย่างของ DataContext ( LINQ to SQL ) ที่ทำในรูปแบบซิงเกิลแม้ว่าข้อเท็จจริงที่ว่า DataContext จะไม่ปลอดภัยและเป็นวัตถุหน่วยของงาน


หลายคนเขียนโค้ดหลายเธรดที่ไม่ปลอดภัยหมายความว่าเราควรกำจัดเธรดหรือไม่
Dainius

@Dainius: จริง ๆ แล้วใช่ IMO โมเดลการทำงานพร้อมกันที่เป็นค่าเริ่มต้นควรเป็นแบบหลายกระบวนการโดยมีวิธีสำหรับสองกระบวนการในการ (1) ส่งข้อความถึงกันได้อย่างง่ายดายและ / หรือ (2) แบ่งปันหน่วยความจำตามความต้องการ การทำเธรดมีประโยชน์ถ้าคุณต้องการแชร์ทุกอย่างแต่คุณไม่ต้องการสิ่งนั้นจริงๆ มันอาจถูกจำลองด้วยการแบ่งปันพื้นที่ที่อยู่ทั้งหมด แต่ก็จะถือว่าเป็นรูปแบบการต่อต้าน
cHao

@Dainius: และแน่นอนว่าสมมติว่าการเห็นพ้องด้วยความซื่อสัตย์ต่อความดีนั้นเป็นสิ่งจำเป็น บ่อยครั้งที่คุณต้องการที่จะทำสิ่งใดสิ่งหนึ่งในขณะที่คุณรอให้ระบบปฏิบัติการทำอย่างอื่น (อัปเดต UI ในขณะที่อ่านจากซ็อกเก็ต) async API ที่เหมาะสมสามารถทำให้การทำเกลียวแบบเต็มโดยไม่จำเป็นในกรณีส่วนใหญ่
cHao

ดังนั้นถ้าคุณมีเมทริกซ์มากของข้อมูลที่คุณจำเป็นต้องดำเนินการจะดีกว่าที่จะทำอย่างนั้นในหัวข้อเดียวเพราะหลาย ๆ คนอาจจะทำผิดว่า ..
Dainius

@Dainius: ในหลาย ๆ กรณีใช่ (การทำเกลียวอาจทำให้คุณช้าลงได้ถ้าคุณเพิ่มการซิงโครไนซ์ลงในการผสมนี่เป็นตัวอย่างที่ดีว่าทำไม multithreading ไม่ควรเป็นความคิดแรกของคนส่วนใหญ่) ในกรณีอื่น ๆ มันจะดีกว่าถ้ามีสองกระบวนการที่แบ่งปันกัน สิ่งที่จำเป็น แน่นอนคุณจะต้องจัดให้มีกระบวนการในการแบ่งปันหน่วยความจำ แต่ตรงไปตรงมาฉันเห็นว่าเป็นสิ่งที่ดี - ดีกว่าอย่างน้อยกว่าการตั้งค่าเริ่มต้นเป็นโหมดแชร์ทุกอย่าง มันต้องการให้คุณพูดอย่างชัดเจน (และอย่างที่ควรจะรู้) ว่าส่วนใดที่ใช้ร่วมกันและส่วนใดที่จะต้องปลอดภัยต่อเธรด
cHao

6

นี่เป็นอีกเรื่องเกี่ยวกับซิงเกิลตันที่ยังไม่มีใครพูด

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

GoF Singletons ตรงข้ามกับ IoC Singletons ควรจะเปิดเผย "singletonity" ในอินเตอร์เฟสผ่านเมธอด getInstance () และเพื่อให้พวกเขาประสบทุกสิ่งที่กล่าวมาข้างต้น


3
ในความคิดของฉัน "singletonity" เป็นรายละเอียดของสภาพแวดล้อมแบบรันไทม์และไม่ควรพิจารณาโดยโปรแกรมเมอร์ที่เขียนโค้ดคลาส แต่เป็นการพิจารณาโดย USER ชั้นเรียน เฉพาะ USER เท่านั้นที่รู้ว่าจำเป็นต้องใช้อินสแตนซ์จำนวนเท่าใด
Earth Engine

5

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


! น่ากลัว เห็นด้วยอย่างสมบูรณ์! แต่คุณสามารถและควรจะทำอย่างละเอียดมากขึ้น เช่นการขยายแพทเทิร์นการออกแบบที่มักจะละเลยและวิธีการ "ใช้อย่างถูกต้องและน้อยที่สุด" ใช้ซิงเกิลตัน พูดง่ายกว่าทำ ! : P
cregox

โดยทั่วไปทางเลือกคือการส่งผ่านวัตถุรอบ ๆ โดยใช้วิธีพารามิเตอร์แทนที่จะเข้าถึงพวกเขาผ่านรัฐทั่วโลก
LegendLength

5

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

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

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

ปัญหาที่เกิดจากการทดสอบแบบซิงเกิลเป็นอาการของกรณีการใช้งาน / สภาพแวดล้อมการใช้งานรหัสเดียวฮาร์ด ชุดการทดสอบและการทดสอบจำนวนมากนั้นแยกจากกันและแยกสิ่งที่ไม่เข้ากันกับการเข้ารหัสแบบซิงเกิล


4

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


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

ฉันมีแอปพลิเคชั่นขนาดใหญ่ที่มี "MainScreen" หน้าจอนี้เปิดขึ้นแบบฟอร์ม modal / non modal windows / UI ขนาดเล็กจำนวนมาก IMHO ฉันรู้สึกว่าหน้าจอหลักควรเป็นซิงเกิลตันดังนั้นตัวอย่างเช่นวิดเจ็ตในมุมที่ห่างไกลของแอปพลิเคชันต้องการแสดงสถานะในแถบสถานะของหน้าจอหลักทั้งหมดที่ต้องทำคือ MainScreen.getInstance () setStatus () "ข้อความบางส่วน"); คุณเสนออะไรเป็นทางเลือก? ผ่านหน้าจอหลักไปทั่วแอปพลิเคชัน? : D
Salvin Francis

2
@SalvinFrancis: สิ่งที่ฉันอยากจะแนะนำคือคุณหยุดมีวัตถุที่สนใจสิ่งที่พวกเขาไม่ควรสนใจและแอบเข้าไปในแอพเพื่อยุ่งกับกันและกัน :) ตัวอย่างของคุณจะดีขึ้นเมื่อมีเหตุการณ์ เมื่อคุณทำกิจกรรมอย่างถูกต้องวิดเจ็ตไม่จำเป็นต้องสนใจว่าจะมีหน้าจอหลักหรือไม่ มันเพิ่งออกอากาศ "เฮ้เกิดอะไรขึ้น" และอะไรก็ตามที่สมัครเป็นสมาชิกของ "เหตุการณ์ที่เกิดขึ้น" (ไม่ว่าจะเป็น MainScreen, WidgetTest หรืออย่างอื่นเลย!) ตัดสินใจว่าจะตอบอย่างไร นั่นเป็นวิธีที่ OOP ควรจะทำต่อไป :)
cHao

1
@Salvin พิจารณาว่าการให้เหตุผลเกี่ยวกับ MainScreen นั้นยากเพียงใดเมื่อทำการดีบั๊กหากมีการอัปเดตโดย "เงียบ ๆ " โดยหลาย ๆ องค์ประกอบ ตัวอย่างของคุณเป็นเหตุผลที่สมบูรณ์แบบว่าทำไมซิงเกิลตันถึงไม่ดี
LegendLength
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.