ซิงเกิล: ควรใช้อย่างไร


291

แก้ไข: จากคำถามอื่นฉันให้คำตอบที่มีลิงก์ไปยังคำถาม / คำตอบมากมายเกี่ยวกับซิงเกิล: ข้อมูลเพิ่มเติมเกี่ยวกับซิงเกิลที่นี่:

ดังนั้นฉันได้อ่านหัวข้อSingletons: การออกแบบที่ดีหรือไม้ยันรักแร้?
และการโต้เถียงยังคงโกรธ

ฉันเห็นว่า Singletons เป็นรูปแบบการออกแบบ (ดีและไม่ดี)

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

ดังนั้นคำถามหลักที่ต้องตอบคือ:

  • เมื่อใดที่คุณควรใช้ซิงเกิลตัน
  • คุณใช้งานซิงเกิลตันได้อย่างไร

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


เพื่อให้ลูกบอลกลิ้ง:
ฉันจะจับมือและพูดว่านี่คือสิ่งที่ฉันใช้ แต่อาจมีปัญหา
ฉันชอบ "Scott Myers" ในการจัดการเรื่องในหนังสือของเขา "Effective C ++"

สถานการณ์ที่ดีสำหรับการใช้ซิงเกิลตัน (ไม่มาก):

  • กรอบการบันทึก
  • เธรดพูลการรีไซเคิล
/*
 * C++ Singleton
 * Limitation: Single Threaded Design
 * See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
 *      For problems associated with locking in multi threaded applications
 *
 * Limitation:
 * If you use this Singleton (A) within a destructor of another Singleton (B)
 * This Singleton (A) must be fully constructed before the constructor of (B)
 * is called.
 */
class MySingleton
{
    private:
        // Private Constructor
        MySingleton();
        // Stop the compiler generating methods of copy the object
        MySingleton(MySingleton const& copy);            // Not Implemented
        MySingleton& operator=(MySingleton const& copy); // Not Implemented

    public:
        static MySingleton& getInstance()
        {
            // The only instance
            // Guaranteed to be lazy initialized
            // Guaranteed that it will be destroyed correctly
            static MySingleton instance;
            return instance;
        }
};

ตกลง. ให้ได้คำวิจารณ์และการใช้งานอื่น ๆ ด้วยกัน
:-)


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

3
ใครบอกว่าเฟรมเวิร์กสามารถมีอินสแตนซ์ตัวบันทึกได้ 1 อินเท่านั้น Singelton หนึ่งตัวเป็นตัวแทนของ Framework Framwork สามารถให้คนตัดไม้เฉพาะคุณได้
มาร์ตินยอร์ค

ใช่. ฉันจะไม่ใช้ Singeltong เป็นเธรดพูล เพียงแค่โยนความคิดออกมาเพื่อจุดประกายคำตอบ
มาร์ตินยอร์ค

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

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

คำตอบ:


182

พวกคุณทุกคนผิด อ่านคำถาม ตอบ:

ใช้ Singleton ถ้า:

  • คุณต้องมีวัตถุประเภทเดียวในระบบ

อย่าใช้ซิงเกิลตันหาก:

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

วิธีการสร้างซิงเกิลตันที่ดีที่สุด:

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

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

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

4
ปิดเพื่อการแก้ไขเปิดสำหรับการขยาย ปัญหาคือคุณไม่สามารถขยายซิงเกิลตันให้เป็น duoton หรือ tripleton มันติดอยู่ในฐานะซิงเกิล
Lee Louviere

2
@ enzom83: เมืองหลวง-S Singleton มีรหัสเพื่อให้แน่ใจว่าเป็นโสด หากคุณต้องการเพียงแค่อินสแตนซ์เดียวคุณสามารถสูญเสียรหัสนั้นและเพียงแค่สร้างหนึ่งอินสแตนซ์ด้วยตัวคุณเอง ... ให้การประหยัดหน่วยความจำของอินสแตนซ์เดียวรวมทั้งการประหยัดจากการลบรหัส Singleness-enforcing ซึ่งหมายความว่า ความสามารถในการสร้างอินสแตนซ์ที่สองหากความต้องการของคุณเปลี่ยนแปลง
cHao

4
"ถ้าคุณต้องการวัตถุประเภทเดียวในระบบ" - "... และไม่ต้องการจำลองวัตถุนั้นในการทดสอบหน่วย"
Cygon

72

ซิงเกิลให้ความสามารถในการรวมสองคุณลักษณะที่ไม่ดีในชั้นหนึ่ง มันผิดทุกอย่างเลย

ซิงเกิลให้คุณ:

  1. การเข้าถึงวัตถุและ
  2. การรับประกันว่าไม่สามารถสร้างวัตถุประเภทนี้ได้มากกว่าหนึ่งรายการ

หมายเลขหนึ่งตรงไปตรงมา ทั่วโลกมักจะไม่ดี เราไม่ควรทำให้วัตถุเข้าถึงได้ทั่วโลกเว้นแต่ว่าเราต้องการมันจริงๆ

หมายเลขสองอาจฟังดูสมเหตุสมผล แต่ลองคิดดู ครั้งสุดท้ายที่คุณ ** ตั้งใจสร้างวัตถุใหม่แทนที่จะอ้างอิงวัตถุที่มีอยู่เมื่อใด เนื่องจากนี่คือการติดแท็ก C ++ ลองใช้ตัวอย่างจากภาษานั้น คุณมักจะเขียนโดยไม่ตั้งใจ

std::ostream os;
os << "hello world\n";

เมื่อคุณตั้งใจจะเขียน

std::cout << "hello world\n";

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

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

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

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

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

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

หากคุณต้องการทั้งสองลักษณะจากนั้น 1) กำหนดให้เป็นซิงเกิลและ 2) แจ้งให้เราทราบว่าคุณต้องการอะไรเพราะฉันมีปัญหาในการจินตนาการกรณีดังกล่าว


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

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

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

2
@ jalf - เป้าหมายของฉันคือเพื่อให้คุณตัวอย่างที่มีประโยชน์ Singleton ในป่าเนื่องจากคุณไม่สามารถจินตนาการใด ๆ ; ฉันเดาว่าคุณไม่เห็นหลายครั้งที่จะใช้มันกับงานปัจจุบันของคุณ ฉันเปลี่ยนเป็นการเขียนโปรแกรมไถหิมะจากแอปพลิเคชั่นธุรกิจเพียงอย่างเดียวเพราะมันทำให้ฉันสามารถใช้ซิงเกิลตันได้ :) j / k ฉันเห็นด้วยกับหลักฐานของคุณว่ามีวิธีที่ดีกว่าในการทำสิ่งเหล่านี้คุณให้ฉันคิดมาก ขอบคุณสำหรับการสนทนา!
J. Polfer

2
การใช้"รูปแบบ" ซิงเกิล ( AHEM! ) "รูปแบบ" เพื่อป้องกันผู้คนจากการอินสแตนซ์มากขึ้นนั้นเป็นเรื่องธรรมดาที่โง่เขลาเพียงเพื่อป้องกันไม่ให้คนทำเช่นนั้นโดยเจตนา เมื่อฉันมีตัวแปรท้องถิ่น foo1 ประเภท Foo ในฟังก์ชั่นเล็ก ๆ ของฉันและต้องการเพียงหนึ่งในฟังก์ชั่นฉันไม่กังวลว่าจะมีคนสร้าง Foo varaible foo2 ตัวที่สองและใช้สิ่งนั้นแทนของเดิม
Thomas Eding

36

ปัญหาของซิงเกิลตันไม่ได้ถูกนำไปใช้ มันคือการที่พวกเขาทำให้เกิดความคิดที่แตกต่างกันสองแนวคิด

1) Singletons จัดเตรียมกลไกการเข้าถึงทั่วโลกให้กับวัตถุ แม้ว่าพวกมันอาจจะเป็นเธรดที่ปลอดภัยกว่าเล็กน้อยหรือน่าเชื่อถือมากกว่าเล็กน้อยในภาษาที่ไม่มีลำดับการกำหนดค่าเริ่มต้นที่ดีการใช้งานนี้ยังคงเทียบเท่ากับคุณธรรมของตัวแปรทั่วโลก มันเป็นตัวแปรระดับโลกที่แต่งขึ้นในรูปแบบที่น่าอึดอัดใจบางอย่าง (foo :: get_instance () แทนที่จะเป็น g_foo พูด) แต่มันให้บริการจุดประสงค์เดียวกันที่แน่นอน

2) ซิงเกิลตันป้องกันหลายอินสแตนซ์ของคลาส มันหายาก, IME, ว่าฟีเจอร์ประเภทนี้ควรถูกอบเข้าชั้นเรียน ปกติแล้วมันจะเป็นบริบทมากกว่า หลายสิ่งหลายอย่างที่ถือเป็นหนึ่งเดียวเท่านั้นจริงๆเกิดขึ้นเป็นเพียงคนเดียว IMO โซลูชันที่เหมาะสมกว่านั้นคือเพียงสร้างเพียงหนึ่งอินสแตนซ์จนกว่าคุณจะรู้ว่าคุณต้องการมากกว่าหนึ่งอินสแตนซ์


6
ตกลง ความผิดสองอย่างอาจทำให้ถูกต้องตามความเป็นจริงในโลกแห่งความจริง แต่ในการเขียนโปรแกรมการผสมผสานความคิดที่ไม่ดีทั้งสองไม่ได้ส่งผลดี
jalf

27

สิ่งหนึ่งที่มีรูปแบบ: ไม่ได้คุย พวกเขามีทุกกรณีเมื่อพวกเขามีประโยชน์และเมื่อพวกเขาล้มเหลว

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

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

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


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

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

1
+1 สำหรับการชี้ความเจ็บปวดของซิงเกิลตันในการทดสอบ
DevSolar

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

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

13

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

Java โดยเฉพาะใช้ singletons แทนตัวแปรทั่วโลกเนื่องจากทุกอย่างจะต้องอยู่ในชั้นเรียน ตัวแปรที่ใกล้เคียงที่สุดกับตัวแปรโกลบอลคือตัวแปรสแตติกสาธารณะซึ่งอาจใช้ราวกับว่ามันเป็นโกลบอลimport static

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

ภาษาเช่น Python และ Ruby ใช้ singletons น้อยมากเพราะคุณสามารถใช้ตัวแปรโกลบอลภายในโมดูลแทน

ดังนั้นเมื่อไรดี / ไม่ดีในการใช้ซิงเกิล? ค่อนข้างดีเมื่อมันจะดี / ไม่ดีที่จะใช้ตัวแปรทั่วโลก


เมื่อใดที่ตัวแปรระดับโลกเป็น "ดี"? บางครั้งพวกเขาก็แก้ปัญหาที่ดีที่สุดสำหรับปัญหา แต่พวกเขาจะไม่ "ดี"
DevSolar

1
ตัวแปรทั่วโลกนั้นดีเมื่อมีการใช้ทุกที่และทุกสิ่งสามารถเข้าถึงได้ การใช้งานเครื่องจักรทัวริงของรัฐเดียวสามารถใช้ประโยชน์จากซิงเกิลตัน
Lee Louviere

ฉันชอบเลเยอร์ของการอ้อมในคำตอบนี้: "เมื่อมันจะดี / ไม่ดีที่จะใช้ทั่วโลก" ทั้ง DevSolar และ Lee Louviere ได้รับคุณค่าที่พวกเขาเห็นด้วยแม้ว่าจะเป็นคำตอบ แต่ก็ไม่รู้ว่าใครจะแสดงความคิดเห็น
Praxeolitic

6

การออกแบบ C ++ ที่ทันสมัยโดย Alexandrescu มีซิงเกิล - เธรดทั่วไปที่ปลอดภัยและสืบทอดได้

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


6
  • คุณใช้งานซิงเกิลตันได้อย่างไร

มีปัญหาหนึ่งที่ฉันไม่เคยเห็นมาก่อนมีอะไรบางอย่างที่ฉันพบเจอในงานก่อนหน้านี้ เรามี C ++ singletons ที่ใช้ร่วมกันระหว่าง DLL และกลไกปกติของการสร้างความมั่นใจว่าอินสแตนซ์เดียวของคลาสไม่ทำงาน ปัญหาคือว่าแต่ละ DLL ได้รับชุดตัวแปรคงที่ของตัวเองพร้อมกับ EXE หากฟังก์ชัน get_instance ของคุณเป็นแบบอินไลน์หรือเป็นส่วนหนึ่งของห้องสมุดแบบคงที่ DLL แต่ละอันจะปิดท้ายด้วยสำเนา "singleton" ของตัวเอง

วิธีแก้ไขคือตรวจสอบให้แน่ใจว่ามีการกำหนดรหัสซิงเกิลตันใน DLL หรือ EXE เดียวเท่านั้นหรือสร้างตัวจัดการแบบซิงเกิลที่มีคุณสมบัติเหล่านั้นเพื่อแยกอินสแตนซ์ออก


10
อ๋อใช่ฉันได้ยินมาว่าคุณชอบเพลงซิงเกิลดังนั้นฉันจึงสร้างซิงเกิลตันให้กับซิงเกิลของคุณเพื่อให้คุณสามารถต่อต้านลายในขณะที่คุณต่อต้านลาย
Eva

@Eva ใช่บางอย่างเช่นนั้น ฉันไม่ได้สร้างปัญหาฉันแค่ต้องทำให้มันใช้งานได้
Mark Ransom

5

ตัวอย่างแรกไม่ได้เป็นเธรดที่ปลอดภัย - ถ้าสองเธรดเรียกใช้ getInstance ในเวลาเดียวกันสแตติกนั้นจะเป็น PITA Mutex บางรูปแบบจะช่วยได้


ใช่ที่ระบุไว้ในความคิดเห็นด้านบน: * ข้อ จำกัด : การออกแบบเธรดเดี่ยว * ดู: aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf * สำหรับปัญหาที่เกี่ยวข้องกับการล็อคแอปพลิเคชั่

คลาสสิกซิงเกิลตันที่มี getInstance เป็นวิธีการคงที่และวิธีการอินสแตนซ์สำหรับการดำเนินการอื่น ๆ ไม่สามารถทำให้เธรดปลอดภัยได้ (ดีจนกว่าคุณจะทำให้มันเป็นด้ายจัดเก็บในท้องถิ่นเดียวต่อด้ายตันใช้ ... )
Tobi

แม้ใน c ++ 11 หรือใหม่กว่า
hg_git

5

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

แง่มุมที่มีประโยชน์ของ singletons:

  1. ขี้เกียจหรือการเริ่มต้นล่วงหน้า
  2. มีประโยชน์สำหรับวัตถุที่ต้องมีการตั้งค่าและ / หรือสถานะ

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

ในที่สุดโรงงานก็ปล่อยให้เทคโนโลยีการฉีดพึ่งพาเช่นสปริงเป็นต้น


3

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

หากคุณทำสิ่งนี้ทุกครั้งประสิทธิภาพจะลดลงมาก เมื่อใช้ในซิงเกิลคุณจะต้องกดปุ่มนั้นหนึ่งครั้งจากนั้นการโทรที่ตามมาทั้งหมดไม่จำเป็น


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

3

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

ดังนั้นแม้แต่ตัวอย่างของตัวบันทึกหรือเธรดพูลนั้นไม่ถูกต้องและควรถูกแทนที่ด้วยกลยุทธ์


คนตัดไม้ไม่ควรเป็นโสด ระบบการส่งข้อความ "ออกอากาศ" ทั่วไปควรเป็น คนตัดไม้เป็นสมาชิกของข้อความออกอากาศ
CashCow

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

3

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


3

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



2

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

#include<iostream>
#include<mutex>

using namespace std;
std::mutex mtx;

class MySingleton{
private:
    static MySingleton * singletonInstance;
    MySingleton();
    ~MySingleton();
public:
    static MySingleton* GetInstance();
    MySingleton(const MySingleton&) = delete;
    const MySingleton& operator=(const MySingleton&) = delete;
    MySingleton(MySingleton&& other) noexcept = delete;
    MySingleton& operator=(MySingleton&& other) noexcept = delete;
};

MySingleton* MySingleton::singletonInstance = nullptr;
MySingleton::MySingleton(){ };
MySingleton::~MySingleton(){
    delete singletonInstance;
};

MySingleton* MySingleton::GetInstance(){
    if (singletonInstance == NULL){
        std::lock_guard<std::mutex> lock(mtx);
        if (singletonInstance == NULL)
            singletonInstance = new MySingleton();
    }
    return singletonInstance;
}

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


2
นั่นไม่ดีกว่าแน่นอน 1: คุณไม่ได้กำหนดซีแมนทิกส์เป็นเจ้าของโดยใช้ตัวชี้ คุณไม่ควรใช้พอยน์เตอร์ใน C ++ จนกว่าคุณจะพร้อมที่จะจัดการมัน 2: การใช้งานการตรวจสอบการล็อคสองครั้งของคุณนั้นล้าสมัยและมีวิธีการที่ดีกว่าในการทำเช่นนี้ 3: ความคิดเห็นของคุณเกี่ยวกับการทำลายนั้นไร้เดียงสา การเรียกคืนหน่วยความจำไม่ใช่จุดประสงค์ในการใช้งาน destructor ที่เกี่ยวกับการล้างข้อมูล คำแนะนำสำหรับรุ่นที่ดีกว่า: ดูคำถาม รุ่นที่นำเสนอนั้นดีกว่ามากแล้ว
Martin York

1

ฉันใช้ซิงเกิลตันเป็นแบบทดสอบการสัมภาษณ์

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


45
กฎที่ยากและรวดเร็วเกี่ยวกับการจ้างงานจะทำให้คุณพลาดโอกาสในการมีพนักงานหลากหลาย
Karl

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

3
สำหรับสมุดบันทึก - คำตอบของฉันคือการใช้ลิ้นในแก้ม ในขั้นตอนการสัมภาษณ์จริงของฉันฉันพยายามประเมินว่าเราจะต้องติวคนในภาษา C ++ หรือไม่และจะยากขนาดไหน ผู้สมัครที่ฉันชอบบางคนเป็นคนที่ไม่รู้จัก C ++ ทั้งภายในและภายนอก แต่ฉันสามารถพูดคุยกับพวกเขาได้อย่างยอดเยี่ยมเกี่ยวกับเรื่องนี้
Matt Cruikshank

4
ลงคะแนน จากประสบการณ์ส่วนตัวของฉันโปรแกรมเมอร์อาจไม่สามารถตั้งชื่อรูปแบบอื่นใดได้นอกจาก Singleton แต่นั่นไม่ได้หมายความว่าเขาใช้ Singletons โดยส่วนตัวแล้วฉันใช้ซิงเกิลตันในรหัสของฉันก่อนที่ฉันเคยได้ยินพวกเขา (ฉันเรียกพวกเขาว่า "ฉลาดกว่ากลมกลืน" - ฉันรู้ว่าโลกคืออะไร) เมื่อฉันเรียนรู้เกี่ยวกับพวกเขาเมื่อฉันเรียนรู้เกี่ยวกับข้อดีข้อเสีย - ฉันเลิกใช้พวกเขา ทันใดนั้นการทดสอบหน่วยกลายเป็นเรื่องที่น่าสนใจสำหรับฉันเมื่อฉันหยุด ... นั่นทำให้ฉันเป็นโปรแกรมเมอร์ที่แย่ลงหรือไม่ Pfff ...
Paulius

3
ฉันยัง downvoting สำหรับ "ชื่อรูปแบบการออกแบบบางอย่าง" คำถามไร้สาระ การออกแบบเป็นเรื่องเกี่ยวกับการทำความเข้าใจวิธีการใช้รูปแบบการออกแบบไม่เพียง แต่สามารถแยกชื่อออกได้ ตกลงว่าอาจไม่รับประกัน downvote แต่คำตอบนี้คือ troll-ish
CashCow

0

ต่อต้านการใช้งาน:

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


Downvoted ด้วยเหตุผล 2 ประการ: 1. Singleton สามารถใช้อินสแตนซ์ polymorphic ภายใน (ตัวอย่างเช่น Global Logger ใช้กลยุทธ์ polymorphic ของการกำหนดเป้าหมาย) 2. สามารถมี typedef สำหรับชื่อ singleton ได้ดังนั้นโค้ดขึ้นอยู่กับ typedef
topright gamedev

ฉันลงเอยด้วยการสร้างเวอร์ชั่นของซิงเกิลตันเพื่อขยายได้โดยใช้รูปแบบเทมเพลตที่เกิดขึ้นซ้ำ ๆ
Zachary Kraus

0

ฉันคิดว่านี่เป็นรุ่นที่แข็งแกร่งที่สุดสำหรับ C #:

using System;
using System.Collections;
using System.Threading;

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Same instance?
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 server requests
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // "Singleton"

  class LoadBalancer
  {
    private static LoadBalancer instance;
    private ArrayList servers = new ArrayList();

    private Random random = new Random();

    // Lock synchronization object
    private static object syncLock = new object();

    // Constructor (protected)
    protected LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      // Support multithreaded applications through
      // 'Double checked locking' pattern which (once
      // the instance exists) avoids locking each
      // time the method is invoked
      if (instance == null)
      {
        lock (syncLock)
        {
          if (instance == null)
          {
            instance = new LoadBalancer();
          }
        }
      }

      return instance;
    }

    // Simple, but effective random load balancer

    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

นี่คือ. NET เวอร์ชั่นที่ดีที่สุด :

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

  // MainApp test application

  class MainApp
  {

    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Confirm these are the same instance
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 requests for a server
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // Singleton

  sealed class LoadBalancer
  {
    // Static members are lazily initialized.
    // .NET guarantees thread safety for static initialization
    private static readonly LoadBalancer instance =
      new LoadBalancer();

    private ArrayList servers = new ArrayList();
    private Random random = new Random();

    // Note: constructor is private.
    private LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      return instance;
    }

    // Simple, but effective load balancer
    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

คุณสามารถหารูปแบบนี้ที่dotfactory.com


3
คุณสามารถตัดส่วนที่ไม่เกี่ยวข้องกับ Singletons ออกเป็นพิเศษเพื่อให้อ่านรหัสได้ง่ายขึ้น
Martin York

นอกจากนี้เวอร์ชันแรกของคุณจะไม่ปลอดภัยสำหรับเธรดเนื่องจากอาจมีการเรียงลำดับการอ่าน / เขียนที่เป็นไปได้ ดูstackoverflow.com/questions/9666/…
Thomas Danecker

5
เอ่อ ... ผิดภาษาเหรอ? คำถามคือสวยติดแท็กอย่างเห็นได้ชัดc ++
DevSolar

0

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

ซิงเกิลตันเป็นการใช้งานสำหรับออบเจ็กต์ที่สามารถเข้าถึงได้ทั่วโลก (GAO นับจากนี้เป็นต้นไป) แม้ว่า GAO ทั้งหมดจะไม่ใช่ซิงเกิลตัน

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

การประเมิน Lazy-loading / lazy เป็นแนวคิดที่แตกต่างและซิงเกิลตันก็มักจะนำไปใช้เช่นกัน มันมาพร้อมกับปัญหาของตัวเองมากมายโดยเฉพาะอย่างยิ่งเธรดความปลอดภัยและปัญหาถ้ามันล้มเหลวโดยมีข้อยกเว้นเช่นว่าสิ่งที่ดูเหมือนความคิดที่ดีในเวลานั้นกลับกลายเป็นว่าไม่ดีนัก (บิตเช่นการใช้งาน COW ในสตริง)

โดยที่ในใจ GOAs สามารถเริ่มต้นได้เช่นนี้:

namespace {

T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;

}

int main( int argc, char* argv[])
{
   T1 t1(args1);
   T2 t2(args2);
   T3 t3(args3);
   T4 t4(args4);

   pt1 = &t1;
   pt2 = &t2;
   pt3 = &t3;
   pt4 = &t4;

   dostuff();

}

T1& getT1()
{
   return *pt1;
}

T2& getT2()
{
   return *pt2;
}

T3& getT3()
{
  return *pt3;
}

T4& getT4()
{
  return *pt4;
}

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

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


0

ฉันยังไม่เข้าใจว่าทำไมจึงต้องเป็นซิงเกิลในระดับโลก

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

ฉันไม่เห็นว่าทำไมฟังก์ชั่นนี้จึงไม่ดี


ฉันไม่เข้าใจว่าทำไมคุณคิดว่ามันต้องเป็นโลก
Martin York

ตามหัวข้อนี้ทุกคนบอกว่าซิงเกิลต้องเป็นโลก
Zachary Kraus

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

ดังนั้นตัวแปรสแตติกส่วนตัวยังคงเป็นส่วนกลางเนื่องจาก "ระยะเวลาการจัดเก็บแบบคงที่"?
Zachary Kraus

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

0

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

ฉันแน่ใจว่ามีวิธีแก้ไขปัญหาอื่น ๆ แต่ฉันพบว่ามีประโยชน์และง่ายต่อการใช้งาน


0

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

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


-1

ในแอพเดสก์ท็อป (ฉันรู้ว่ามี แต่ไดโนเสาร์ของเราเท่านั้นที่เขียนสิ่งเหล่านี้อีกต่อไป!) พวกมันจำเป็นสำหรับการตั้งค่าแอพพลิเคชั่นระดับโลกที่ไม่เปลี่ยนแปลง - ภาษาผู้ใช้เส้นทางที่จะช่วยเหลือไฟล์ .

แก้ไข - แน่นอนว่าสิ่งเหล่านี้ควรเป็นแบบอ่านอย่างเดียว!


แต่นี่เป็นการขอทานคำถาม; ทำไมภาษาของผู้ใช้และพา ธ ไปยังไฟล์ความช่วยเหลือต้องเป็นวิธีการแบบอินสแตนซ์เลย ?
DrPizza

2
เรามีรูปทรงกลมสำหรับสิ่งนั้น มีความจำเป็นในการทำให้พวกเขา singletons ไม่มี
jalf

ตัวแปรโกลบอล - แล้วคุณจะทำให้มันเป็นอนุกรมจาก registry / databse ได้อย่างไร? คลาส Gobal - แล้วคุณจะมั่นใจได้อย่างไรว่ามีเพียงหนึ่งในนั้น?
Martin Beckett

@mgb: คุณทำให้เป็นอนุกรมโดยการอ่านค่าจากรีจีสทรี / ฐานข้อมูลและจัดเก็บไว้ในตัวแปรทั่วโลก (ซึ่งควรทำที่ด้านบนสุดของฟังก์ชั่นหลักของคุณ) คุณแน่ใจว่ามีเพียงวัตถุเดียวของคลาสโดยการสร้างเพียงหนึ่งวัตถุของคลาส ... จริง ๆ ... มันยากที่จะ 'grep -rn "new \ + global_class_name" ? จริงๆ?
Paulius

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

-1

การดำเนินการอื่น

class Singleton
{
public:
    static Singleton& Instance()
    {
        // lazy initialize
        if (instance_ == NULL) instance_ = new Singleton();

        return *instance_;
    }

private:
    Singleton() {};

    static Singleton *instance_;
};

3
มันช่างน่ากลัวจริงๆ โปรดดูที่: stackoverflow.com/questions/1008019/c-singleton-design-pattern/ …เพื่อการเริ่มต้นที่ขี้เกียจและการทำลายล้างที่กำหนดขึ้นที่สำคัญยิ่งกว่า
มาร์ตินยอร์

หากคุณกำลังจะใช้พอยน์เตอร์Instance()ควรส่งคืนพอยน์เตอร์ไม่ใช่การอ้างอิง ภายในของคุณไฟล์เริ่มต้นอินสแตนซ์โมฆะ:.cpp Singleton* Singleton::instance_ = nullptr;และInstance()ควรดำเนินการตาม: if (instance_ == nullptr) instance_ = new Singleton(); return instance_;.
Dennis
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.