บางคนคิดว่ารูปแบบซิงเกิลนั้นเป็นรูปแบบต่อต้านเสมอ คุณคิดอย่างไร?
บางคนคิดว่ารูปแบบซิงเกิลนั้นเป็นรูปแบบต่อต้านเสมอ คุณคิดอย่างไร?
คำตอบ:
การวิพากษ์วิจารณ์หลักสองข้อของซิงเกิลตกลงไปในสองค่ายจากสิ่งที่ฉันสังเกตเห็น:
เป็นผลมาจากทั้งสองนี้วิธีการทั่วไปคือการใช้สร้างวัตถุภาชนะกว้างที่จะถืออินสแตนซ์เดียวของชั้นเรียนเหล่านี้และมีเพียงวัตถุภาชนะปรับเปลี่ยนประเภทของชั้นเรียนเหล่านี้ในขณะที่ชั้นเรียนอื่น ๆ อีกมากมายสามารถได้รับ วัตถุภาชนะ
ฉันยอมรับว่ามันเป็นรูปแบบการต่อต้าน ทำไม? เพราะมันช่วยให้โค้ดของคุณโกหกเกี่ยวกับการพึ่งพามันและคุณไม่สามารถเชื่อถือโปรแกรมเมอร์คนอื่น ๆ ที่จะไม่แนะนำสถานะที่ไม่แน่นอนในซิงเกิลตันที่ไม่เปลี่ยนรูปก่อนหน้านี้ของคุณ
คลาสอาจมีตัวสร้างที่ใช้สตริงเท่านั้นดังนั้นคุณคิดว่ามันเป็นอินสแตนซ์ในการแยกและไม่มีผลข้างเคียง อย่างไรก็ตามอย่างเงียบ ๆ มันกำลังสื่อสารกับวัตถุแบบซิงเกิลที่มีอยู่ทั่วโลกบางชนิดดังนั้นเมื่อใดก็ตามที่คุณสร้างคลาสขึ้นมันจะมีข้อมูลที่แตกต่างกัน นี่เป็นปัญหาใหญ่ไม่เพียง แต่สำหรับผู้ใช้ API ของคุณ แต่ยังสำหรับการทดสอบความสามารถของโค้ดด้วย ในการทดสอบรหัสอย่างถูกต้องคุณจะต้องจัดการไมโครและระวังสถานะโลกในซิงเกิลตันเพื่อรับผลการทดสอบที่สอดคล้องกัน
รูปแบบ Singleton นั้นเป็นเพียงตัวแปรเริ่มต้นทั่วโลกอย่างเฉื่อยชา ตัวแปรทั่วโลกนั้นโดยทั่วไปแล้วถือว่าชั่วร้ายเพราะอนุญาตให้ทำสิ่งที่น่ากลัวในระยะห่างระหว่างส่วนที่ไม่เกี่ยวข้องกันของโปรแกรม อย่างไรก็ตาม IMHO ไม่มีอะไรผิดปกติกับตัวแปรโกลบอลที่ถูกตั้งค่าครั้งเดียวจากที่เดียวซึ่งเป็นส่วนหนึ่งของรูทีนการกำหนดค่าเริ่มต้นของโปรแกรม (ตัวอย่างเช่นโดยการอ่านไฟล์ config หรืออาร์กิวเมนต์บรรทัดคำสั่ง) และถือว่าเป็นค่าคงที่หลังจากนั้น การใช้ตัวแปรทั่วโลกเช่นนี้แตกต่างกันเฉพาะในตัวอักษรไม่ใช่ในใจจากการมีค่าคงที่ที่มีชื่อที่ประกาศ ณ เวลารวบรวม
ในทำนองเดียวกันความคิดเห็นของฉันของ Singletons ก็คือพวกเขาไม่ดีถ้าหากพวกเขาคุ้นเคยกับการผ่านสถานะที่ไม่แน่นอนระหว่างส่วนต่าง ๆ ของโปรแกรม หากพวกเขาไม่ได้มีสถานะที่ไม่แน่นอนหรือถ้ารัฐที่ไม่แน่นอนที่พวกเขามีอยู่ห่อหุ้มอย่างสมบูรณ์เพื่อให้ผู้ใช้ของวัตถุไม่จำเป็นต้องรู้เกี่ยวกับมันแม้ในสภาพแวดล้อมแบบมัลติเธรดแล้วก็ไม่มีอะไรผิดปกติกับพวกเขา
ฉันเคยเห็นซิงเกิลจำนวนมากในโลก PHP ฉันจำกรณีการใช้งานใด ๆ ที่ฉันพบว่ารูปแบบไม่เหมาะสม แต่ฉันคิดว่าฉันมีความคิดเกี่ยวกับแรงจูงใจว่าทำไมคนถึงใช้มัน
โสดอินสแตนซ์
"ใช้คลาส C อินสแตนซ์เดียวทั่วทั้งแอปพลิเคชัน"
นี่เป็นข้อกำหนดที่สมเหตุสมผลเช่นสำหรับ "การเชื่อมต่อฐานข้อมูลเริ่มต้น" ไม่ได้หมายความว่าคุณจะไม่สร้างการเชื่อมต่อฐานข้อมูลครั้งที่สอง แต่ก็หมายความว่าคุณมักจะทำงานกับการเชื่อมต่อเริ่มต้น
โสดinstantiation
"ไม่อนุญาตให้คลาส C ถูกสร้างอินสแตนซ์มากกว่าหนึ่งครั้ง (ต่อกระบวนการ, ต่อคำขอ, ฯลฯ )"
สิ่งนี้มีความเกี่ยวข้องเฉพาะเมื่ออินสแตนซ์ชั้นเรียนนั้นมีผลข้างเคียงที่ขัดแย้งกับอินสแตนซ์อื่น
บ่อยครั้งที่ความขัดแย้งเหล่านี้สามารถหลีกเลี่ยงได้โดยการออกแบบองค์ประกอบใหม่เช่นโดยการกำจัดผลข้างเคียงจากตัวสร้างคลาส หรือพวกเขาสามารถแก้ไขได้ในวิธีอื่น แต่อาจมีกรณีการใช้งานที่ถูกกฎหมาย
คุณควรพิจารณาด้วยว่าข้อกำหนด "เพียงหนึ่งข้อ" จริงๆหมายถึง "หนึ่งต่อกระบวนการ" หรือไม่ เช่นสำหรับการทำงานพร้อมกันของทรัพยากรความต้องการค่อนข้าง "หนึ่งต่อทั้งระบบข้ามกระบวนการ" และไม่ใช่ "หนึ่งต่อกระบวนการ" และสำหรับสิ่งอื่น ๆ มันค่อนข้างเป็น "บริบทแอปพลิเคชัน" และคุณเพิ่งมีบริบทแอปพลิเคชันหนึ่งรายการต่อกระบวนการ
มิฉะนั้นไม่จำเป็นต้องบังคับใช้สมมติฐานนี้ การบังคับใช้สิ่งนี้ยังหมายความว่าคุณไม่สามารถสร้างอินสแตนซ์แยกต่างหากสำหรับการทดสอบหน่วย
การเข้าถึงระดับโลก
สิ่งนี้จะถูกต้องก็ต่อเมื่อคุณไม่มีโครงสร้างพื้นฐานที่เหมาะสมในการส่งวัตถุไปยังสถานที่ที่ใช้ นี่อาจหมายความว่าเฟรมเวิร์กหรือสภาพแวดล้อมของคุณแย่ แต่อาจไม่อยู่ในอำนาจของคุณในการแก้ไข
ราคามีเพศสัมพันธ์แน่นการอ้างอิงที่ซ่อนอยู่และทุกอย่างที่ไม่ดีเกี่ยวกับสถานะโลก แต่คุณอาจต้องทนทุกข์ทรมานกับสิ่งเหล่านี้
ขี้เกียจ instantiation
นี่ไม่ใช่ส่วนที่จำเป็นของซิงเกิล แต่ดูเหมือนจะเป็นวิธีที่นิยมใช้กันมากที่สุด แต่ในขณะที่การสร้างอินสแตนซ์ขี้เกียจเป็นสิ่งที่ดีที่มีอยู่คุณไม่จำเป็นต้องใช้ซิงเกิลจริงๆเพื่อให้บรรลุผลนั้น
การนำไปใช้โดยทั่วไปคือคลาสที่มีคอนสตรัคเตอร์ส่วนตัวและตัวแปรอินสแตนซ์แบบคงที่และเมธอด getInstance () แบบสแตติกที่มีการสร้างอินสแตนซ์ขี้เกียจ
นอกเหนือจากปัญหาที่กล่าวมาข้างต้นกัดด้วยความรับผิดชอบหลักเดียวเพราะชั้นเรียนจะควบคุมการสร้างอินสแตนซ์และวงจรชีวิตของตัวเองนอกเหนือจากความรับผิดชอบอื่น ๆ ที่ชั้นเรียนมีอยู่แล้ว
ในหลายกรณีคุณสามารถบรรลุผลลัพธ์เดียวกันโดยไม่ต้องใช้ซิงเกิลตันและไม่มีสถานะโกลบอล แต่คุณควรใช้ฉีดพึ่งพาและคุณอาจต้องการที่จะต้องพิจารณาภาชนะฉีดพึ่งพา
อย่างไรก็ตามมีกรณีการใช้งานที่คุณมีข้อกำหนดที่ถูกต้องต่อไปนี้เหลืออยู่:
ดังนั้นนี่คือสิ่งที่คุณสามารถทำได้ในกรณีนี้:
สร้างคลาส C ที่คุณต้องการสร้างอินสแตนซ์ด้วยตัวสร้างสาธารณะ
สร้างคลาส S ที่แยกต่างหากพร้อมกับตัวแปรอินสแตนซ์แบบคงที่และวิธีการแบบสแตติก S :: getInstance () พร้อมด้วยการสร้างอินสแตนซ์ขี้เกียจที่จะใช้คลาส C สำหรับอินสแตนซ์
กำจัดผลข้างเคียงทั้งหมดออกจากตัวสร้างของ C แทนให้ใส่ผลข้างเคียงเหล่านี้ในวิธี S :: getInstance ()
หากคุณมีคลาสมากกว่าหนึ่งคลาสที่มีข้อกำหนดด้านบนคุณอาจพิจารณาจัดการอินสแตนซ์ของคลาสด้วยคอนเทนเนอร์เซอร์วิสท้องถิ่นขนาดเล็กและใช้อินสแตนซ์แบบคงที่สำหรับคอนเทนเนอร์เท่านั้น ดังนั้น S :: getContainer () จะให้บริการคอนเทนเนอร์ instantiated ขี้เกียจคุณและคุณได้รับวัตถุอื่น ๆ จากภาชนะ
หลีกเลี่ยงการเรียกใช้ getInstance แบบคงที่ () ที่คุณสามารถทำได้ ใช้การฉีดพึ่งพาแทนหากเป็นไปได้ โดยเฉพาะอย่างยิ่งถ้าคุณใช้วิธีการคอนเทนเนอร์ด้วยวัตถุหลายอย่างที่ขึ้นอยู่กับแต่ละอื่น ๆ สิ่งเหล่านี้ไม่ควรเรียก S :: getContainer ()
สร้างอินเตอร์เฟสที่คลาส C เป็นทางเลือกและใช้สิ่งนี้เพื่อบันทึกค่าส่งคืนของ S :: getInstance ()
(เรายังเรียกสิ่งนี้ว่าซิงเกิลหรือไม่? ฉันจะทิ้งไว้ที่ส่วนความคิดเห็น .. )
ประโยชน์ที่ได้รับ:
คุณสามารถสร้างอินสแตนซ์แยกต่างหากของ C สำหรับการทดสอบหน่วยโดยไม่ต้องสัมผัสสถานะโกลบอลใด ๆ
การจัดการอินสแตนซ์ถูกแยกออกจากคลาส -> การแยกข้อกังวลหลักการความรับผิดชอบเดี่ยว
มันจะค่อนข้างง่ายที่จะให้ S :: getInstance () ใช้คลาสอื่นสำหรับอินสแตนซ์หรือแม้แต่กำหนดคลาสที่จะใช้
โดยส่วนตัวฉันจะใช้ซิงเกิลตอนที่ฉันต้องการ 1, 2, หรือ 3 หรือจำนวน จำกัด ของวัตถุสำหรับคลาสที่ต้องการ หรือฉันต้องการสื่อให้ผู้ใช้ของชั้นเรียนของฉันเห็นว่าฉันไม่ต้องการให้คลาสของฉันถูกสร้างขึ้นเพื่อให้ทำงานได้อย่างถูกต้อง
นอกจากนี้ฉันจะใช้เฉพาะเมื่อฉันต้องการใช้งานเกือบทุกที่ในรหัสของฉันและฉันไม่ต้องการส่งวัตถุเป็นพารามิเตอร์ไปยังแต่ละคลาสหรือฟังก์ชันที่ต้องการ
นอกจากนี้ฉันจะใช้ซิงเกิลตันเท่านั้นหากมันไม่ทำลายความโปร่งใสในการอ้างอิงของฟังก์ชันอื่น ความหมายที่ได้รับจากการป้อนข้อมูลบางอย่างมันจะสร้างผลลัพธ์เดียวกัน นั่นคือฉันไม่ได้ใช้เพื่อสถานะสากล เว้นแต่เป็นไปได้ว่าสถานะโลกจะเริ่มต้นได้ครั้งเดียวและไม่เคยเปลี่ยนแปลง
สำหรับเมื่อไม่ใช้ให้ดูที่ 3 ด้านบนและเปลี่ยนเป็นตรงกันข้าม