มีกรณีการใช้งานสำหรับ singletons ด้วยการเข้าถึงฐานข้อมูลใน PHP?


138

ฉันเข้าถึงฐานข้อมูล MySQL ของฉันผ่าน PDO ฉันตั้งค่าการเข้าถึงฐานข้อมูลและความพยายามครั้งแรกของฉันคือการใช้สิ่งต่อไปนี้:

สิ่งแรกที่ผมคิดว่ามีที่global:

$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'root', 'pwd');

function some_function() {
    global $db;
    $db->query('...');
}

นี่ถือว่าเป็นการปฏิบัติที่ไม่ดี หลังจากค้นหาเพียงเล็กน้อยฉันก็จบลงด้วยรูปแบบซิงเกิลซึ่ง

"ใช้กับสถานการณ์ที่จำเป็นต้องมีอินสแตนซ์เดียวของคลาส"

ตามตัวอย่างในคู่มือเราควรทำสิ่งนี้:

class Database {
    private static $instance, $db;

    private function __construct(){}

    static function singleton() {
        if(!isset(self::$instance))
            self::$instance = new __CLASS__;

        return self:$instance;
    }

    function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd')

        return self::$db;
    }
}

function some_function() {
    $db = Database::singleton();
    $db->get()->query('...');
}

some_function();

ทำไมฉันต้องการคลาสที่ค่อนข้างใหญ่เมื่อฉันสามารถทำสิ่งนี้ได้?

class Database {
    private static $db;

    private function __construct(){}

    static function get() {
        if(!isset(self::$db))
            self::$db = new PDO('mysql:host=127.0.0.1;dbname=toto', 'user', 'pwd');

        return self::$db;
    }
}

function some_function() {
    Database::get()->query('...');
}

some_function();

อันสุดท้ายนี้ทำงานได้อย่างสมบูรณ์แบบและฉันไม่ต้องกังวล$dbอีกต่อไป

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


มีแหล่งข้อมูลและการอภิปรายจำนวนมากในคำถามที่เกี่ยวข้องนี้: 'สิ่งที่เลวร้ายเกี่ยวกับซิงเกิลตัน'
FruitBreak

คำตอบ:


80

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

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

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

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

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

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


15
คุณบอกว่า singletons ลดลงอย่างดีต่อ DI แต่ไม่ใช่ตัวอย่างของคุณที่getInstance(databaseName)ยังคงกระจายการอ้างอิงไปยังแหล่งเก็บข้อมูลส่วนกลางของอินสแตนซ์ตลอดรหัสของคุณ รหัสที่จะโทรgetInstanceควรมีอินสแตนซ์ฉีดเข้าไปด้วยรหัสลูกค้าและไม่ควรโทรgetInstanceในตอนแรก
Will Vousden

1
@ Will Vousden ถูกต้องมันเป็นชนิดของช่องว่างหยุด มันไม่ได้เป็น DI จริงๆ แต่มันก็ใกล้เคียงกันมาก ตัวอย่างเช่นจะเกิดอะไรขึ้นถ้ามันคือ getInstance (สนับสนุนฐานข้อมูล) และอินสแตนซ์ที่ส่งคืนถูกคำนวณโดยอิงตามฐานข้อมูลใดที่ส่งผ่านมา ประเด็นคือเพื่อหลีกเลี่ยงการทำให้บุคคลกลัวด้วยกรอบ DI จนกว่าพวกเขาจะพร้อม
Bill K

320

Singletons มีน้อยมาก - ถ้าไม่บอกว่าไม่มี - ใช้ใน PHP

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

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

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

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

แม้แต่Erich Gammaหนึ่งในนักประดิษฐ์ของรูปแบบซิงเกิลยังสงสัยในรูปแบบนี้ในปัจจุบัน:

"ฉันชอบที่จะทิ้ง Singleton การใช้มันมักจะเป็นกลิ่นของการออกแบบ"

อ่านเพิ่มเติม

หากหลังจากข้างต้นคุณยังต้องการความช่วยเหลือในการตัดสินใจ:

แผนผังการตัดสินใจเดี่ยว


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

4
ขออภัยที่ขัดกับโฟลว์ แต่ DI ไม่ใช่วิธีการแก้ปัญหาที่ใช้ใน Singleton เว้นแต่คุณจะพอใจกับการมีคลาสที่มีพารามิเตอร์ 42 ctor (หรือ 42 setFoo () และ setBar () ที่ต้องใช้ในการโทร งาน). ใช่แอพพลิเคชั่นบางตัวต้องมีคู่นี้และขึ้นอยู่กับสิ่งภายนอกมากมาย PHP เป็นภาษาที่ใช้กาวและบางครั้งก็มีหลายอย่างที่ต้องติดกาวด้วยกัน
StasM

14
@StasM หากคุณมี 42 ctor params หรือต้องการ setters มากมายที่คุณทำผิด ชมการพูดคุยเกี่ยวกับ Clean Code ขออภัยถ้าฉันไม่สามารถอธิบายสิ่งนี้ได้อีกครั้ง รู้สึกอิสระที่จะถามในห้องสนทนา PHP สำหรับข้อมูลเพิ่มเติม
Gordon

@Gordon ห้องสนทนา php อยู่ที่ไหน
user658182

21

ต้องการ singletons ใน PHP ใคร

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

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

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

- ผู้ที่ต้องทำงานกับAPIบริการและเว็บไซต์ของบุคคลที่สาม

หากคุณมองใกล้กว่านี้จะไม่แตกต่างไปจากกรณีก่อนหน้านี้มากเกินไป - API ของบุคคลที่สามบริการเว็บไซต์ต่าง ๆ ก็เหมือนกับฐานรหัสภายนอกที่แยกต่างหากซึ่งคุณไม่สามารถควบคุมได้ สิ่งที่สามารถเกิดขึ้นได้ ดังนั้นด้วยเซสชันเดี่ยว / คลาสผู้ใช้คุณสามารถจัดการการใช้งานเซสชัน / การอนุญาตใด ๆ จากผู้ให้บริการบุคคลที่สามเช่นOpenID , Facebook , Twitterและอื่น ๆ อีกมากมาย - และคุณสามารถทำสิ่งเหล่านี้ทั้งหมดในเวลาเดียวกันจากวัตถุ Ston singleton - ซึ่งสามารถเข้าถึงได้ง่ายในสถานะที่รู้จัก ณ จุดใดก็ตามในโค้ดที่คุณเสียบเข้าไป คุณยังสามารถสร้างหลายเซสชันไปยัง API / บริการบุคคลที่สามที่แตกต่างกันหลายรายการสำหรับผู้ใช้ SAME ในเว็บไซต์ / แอปพลิเคชันของคุณเองและทำสิ่งที่คุณต้องการจะทำกับพวกเขา

แน่นอนว่าทั้งหมดนี้ยังสามารถปรับโทนเสียงด้วยวิธีการดั้งเดิมโดยใช้คลาสและวัตถุปกติ - การจับที่นี่คือซิงเกิลที่เป็น tidier, neater และดังนั้นเนื่องจากการจัดการ / ทดสอบได้ง่ายกว่าเมื่อเทียบกับการใช้คลาส / วัตถุแบบดั้งเดิมในสถานการณ์ดังกล่าว

- ผู้ที่ต้องทำการพัฒนาอย่างรวดเร็ว

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

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

- การคัดค้านสำคัญที่สุดคือทำให้การทดสอบยากขึ้น

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

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

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

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

- ข้อขัดข้องอีกข้อหนึ่งก็คือรอยความทรงจำของมันนั้นสูงกว่า

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

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

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

- การคัดค้านที่ไม่ถูกต้องเช่น 'ทำให้การเชื่อมต่อหลายฐานข้อมูลเป็นไปไม่ได้ / ยากขึ้น'

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

ลองนึกภาพด้านล่างนี้อยู่ในฐานข้อมูลเดี่ยวที่กำหนด:

$ this -> connections = array (); (ไวยากรณ์ผิดฉันเพิ่งพิมพ์มันเพื่อให้รูปภาพ - การประกาศที่เหมาะสมของตัวแปรคือ public $ connections = array () และการใช้มันคือ $ this-> connections ['connectionkey'] โดยธรรมชาติ)

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

$ this -> แบบสอบถาม (QUERYSTRING, 'queryname', $ this-> การเชื่อมต่อ ['โดยเฉพาะ')

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

$ this -> ผลลัพธ์

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

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

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

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

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

คุณจะสังเกตเห็นว่าในบทความส่วนใหญ่ที่มีการทุบซิงเกิลคุณจะเห็นการอ้างอิงถึง 'globals' การเป็น 'ความชั่วร้าย'

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

ตัวอย่างที่ไม่มีที่สิ้นสุดสามารถมอบให้ได้ตั้งแต่ 'กลมเป็นชั่ว' ถึง 'iframes เป็นชั่ว' ย้อนกลับไปเมื่อประมาณ 10 ปีที่แล้วแม้แต่การเสนอการใช้ iframe ในแอปพลิเคชันใดก็ตามก็ตามถือเป็นบาป จากนั้น Facebook, iframes มาทุกหนทุกแห่งและดูว่าเกิดอะไรขึ้น - iframes ก็ไม่ได้เลวร้ายอีกต่อไป

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

ทรัพย์สินที่สำคัญที่สุดของวิศวกรโปรแกรมเมอร์ / coder / ซอฟต์แวร์คือจิตใจที่เปิดกว้างและยืดหยุ่น


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

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

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

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

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

15

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

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


แต่ Singletons สามารถย่อยสลายได้อย่างราบรื่นมากใน DI คลาสแบบสแตติกไม่สามารถทำได้ซึ่งเป็นปัญหาจริงของคลาสแบบสแตติก
Bill K

@ Bill: จริงมาก แต่แล้วนั่นเป็นเหตุผลที่ผมจะสนับสนุนแนวทาง DI ที่จะเริ่มต้นด้วยมากกว่าฟังก์ชั่นหลวมหรือวิธีการคง :)
Will Vousden

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

8

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

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

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


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

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

5

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

มีข้อเสียหลายประการในการใช้ globals ในแอปพลิเคชัน (ซึ่งเป็นสิ่งที่ใช้ในรูปแบบ Singleton แบบดั้งเดิม):

  • ทดสอบหน่วยได้ยาก
  • ปัญหาการฉีดขึ้นอยู่กับ
  • สามารถสร้างปัญหาการล็อค (แอพพลิเคชั่นแบบมัลติเธรด)

ใช้คลาสแบบสแตติกแทนอินสแตนซ์ซิงเกิลให้ข้อเสียบางอย่างเช่นกันเนื่องจากปัญหาที่ใหญ่ที่สุดของซิงเกิลตันคือgetInstanceวิธีสแตติก

คุณสามารถ จำกัด จำนวนอินสแตนซ์ที่คลาสสามารถมีได้โดยไม่ต้องใช้getInstanceวิธีดั้งเดิม:

class Single {

    static private $_instance = false;

    public function __construct() {
        if (self::$_instance)
           throw new RuntimeException('An instance of '.__CLASS__.' already exists');

        self::$_instance = true;
    }

    private function __clone() {
        throw new RuntimeException('Cannot clone a singleton class');
    }

    public function __destruct() {
        self::$_instance = false;
    }

}

$a = new Single;
$b = new Single; // error
$b = clone($a); // error
unset($a);
$b = new Single; // works

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


5

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

ดังนั้นเราจะบรรลุข้อสรุปอะไร?

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

ในทางปฏิบัติซิงเกิลตันเป็นรูปแบบการโต้เถียงที่ค่อนข้างสวย นี่เป็นเพราะส่วนใหญ่:

  • มันมากเกินไป โปรแกรมเมอร์สามเณร grok Singleton ง่ายกว่าพวกเขา grok รูปแบบอื่น ๆ จากนั้นพวกเขาก็จะนำความรู้ใหม่ ๆ ไปใช้ทุกที่แม้ว่าปัญหาในมือจะสามารถแก้ไขได้ดีขึ้นโดยไม่ต้องใช้ซิงเกิลตัน (เมื่อคุณถือค้อนทุกอย่างดูเหมือนเล็บ)
  • ขึ้นอยู่กับภาษาการเขียนโปรแกรมการใช้ซิงเกิลในสุญญากาศลักษณะที่ไม่รั่วไหลสามารถพิสูจน์ได้ว่าเป็นงานไททานิค ) เพียงแค่พยายามค้นหา "การใช้งานซิงเกิล" ที่ชัดเจน "ใน C ++ ฉันกล้าคุณ (ฉันเป็นเจ้าของ Modern C ++ Design อันโด่งดังของ Andrei Alexandrescu ซึ่งเป็นเอกสารที่ยุ่งเหยิงมาก)
  • มันกำหนดปริมาณงานเพิ่มเติมทั้งเมื่อเข้ารหัสรหัสซิงเกิลตันและเมื่อเขียนโค้ดเพื่อเข้าถึงเวิร์กโหลดที่คุณสามารถทำได้โดยไม่ต้องปฏิบัติตามข้อ จำกัด ที่บังคับด้วยตนเองบางประการกับสิ่งที่คุณพยายามทำกับตัวแปรโปรแกรมของคุณ

ดังนั้นเป็นข้อสรุปสุดท้าย:ซิงเกิลของคุณก็โอเค การไม่ได้ใช้ Singleton เป็นเรื่องปกติแล้วเช่นกัน


2

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


2

เมื่อการเขียนโปรแกรมไม่มี "ถูกต้อง" และ "ผิด"; มี "แนวปฏิบัติที่ดี" และ "แนวปฏิบัติที่ไม่ดี"

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

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

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


2

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


1

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

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