Magento 2: การใช้หรือไม่ใช้ ObjectManager โดยตรง?


134

ตกลงดังนั้นเมื่อวานนี้เราได้มีการพูดคุยใหญ่กับคนอื่น ๆ จากชุมชนวีโอไอพีเกี่ยวกับการใช้งานโดยตรงของObjectManagerในชั้นเรียน / แม่แบบ

ฉันได้ทราบถึงสาเหตุที่เราไม่ควรใช้ ObjectManager โดยตรงโดยอ้างถึง Alan Kent :

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

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

จากสิ่งที่ฉันเห็นใน StackExchange ผู้คนจำนวนมากมักจะไปหาวิธีแก้ปัญหาที่ง่าย / สั้น / ไม่แนะนำเช่นบางสิ่งเช่นนี้:

<?php 
//Get Object Manager Instance
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();

//Load product by product id
$product = $objectManager->create('Magento\Catalog\Model\Product')->load($id);

แทนที่จะผ่านขั้นตอนที่เจ็บปวด แต่แนะนำให้ทำดังนี้:

  • สร้างโมดูล
  • ประกาศค่ากำหนด
  • ฉีดพึ่งพา
  • ประกาศวิธีการสาธารณะ

อย่างไรและนี่มาขึ้นเขียง, ไฟล์วีโอไอพี 2 หลักมักจะเรียก ObjectManager โดยตรง ตัวอย่างอย่างรวดเร็วสามารถพบได้ที่นี่: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

ดังนั้นนี่คือคำถามของฉัน:

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


3
ลิงค์ที่เกี่ยวข้อง: mwop.net/blog/2016-04-26-on-locators.html The intent of zend-servicemanager is for use as an Inversion of Control container. It was never intended as a general purpose service locator [...]บิตที่เกี่ยวข้องของมันจะเป็น ซึ่งมันใช้กับ M2 ได้เช่นกัน ตรวจสอบThere are valid use casesส่วนที่ใช้กับที่นี่อีกครั้ง
nevvermind

3
มีการพัฒนา M2 บางช่วงเมื่อ OM อยู่ที่นั่นแล้ว แต่วีโอไอพีทั้งตัวนั้นยังไม่เปลี่ยนไปใช้การฉีดคอนสตรัคเตอร์ ณ จุดนั้นหลายคนแทนที่ Mage :: getSingleton () ด้วย ObjectManager :: getInstance () -> get () ประเพณีดังกล่าวส่วนใหญ่ได้รับการแนะนำในช่วงเวลานั้น ต่อมาการเรียก Mage :: getSingleton () ทั้งหมดถูกแทนที่ด้วยการสร้าง constructor ด้วยเครื่องมือ แต่เครื่องมือไม่รู้จัก ObjectManager :: getInstance () ดังนั้นจึงไม่ได้แทนที่ด้วยการฉีด constructor
Anton Kril

3
สำเนาซ้ำของตัวอย่างวีโอไอพี 2 ที่เป็น
Teja Bhagavan Kollepara

3
@TejabhagavanKollepara คุณอ่านทั้งสองคำถามแล้วหรือยัง มีความคล้ายคลึงกัน แต่ห่างไกลจากการทำสำเนาจากกันและกัน
Raphael at Digital Pianism

คำตอบ:


98

คุณไม่ควรใช้ ObjectManager โดยตรง!

ข้อยกเว้นจากกฎคือ:

  • ในวิธีการมายากลคงเหมือน __wakeup, serializeฯลฯ
  • ในกรณีที่คุณควรจะเข้ากันได้ย้อนหลังของตัวสร้าง
  • ในขอบเขตทั่วโลกเช่นในการติดตั้งการทดสอบการรวม
  • ในชั้นเรียนที่ต้องการเพียงการสร้างวัตถุเช่นโรงงานพร็อกซี ฯลฯ

2
ฉันรู้ว่าฉันไม่ควรใช้มันโดยตรง แต่ทำไมวีโอไอพีถึงทำมัน? ^^
Raphael ที่ Digital Pianism

2
ในตัวอย่างของคุณใช้สำหรับความเข้ากันได้แบบย้อนหลัง
KAndy

เหล่านั้นถูกตั้งค่าสถานะเป็น @deprecated เสมอหรือไม่
ราฟาเอลที่ Pianism ดิจิตอล


5
ฉันรู้ว่ามันเป็นเพียงความสับสน บางทีพวกเขาควรจะพูดว่า "อย่าทำ แต่ต้องระวังว่าเราอาจทิ้งความผิดพลาดบางอย่างที่นี่และที่นั่น";)
Raphael ที่ Digital Pianism

53

เหตุใด M2 จึงเข้าถึงตัวจัดการวัตถุโดยตรงเมื่อเราแนะนำต่อมัน

คำตอบที่โหดร้าย: M2 เป็นพอร์ตของ M1 - ไม่ใช่การเขียนซ้ำทั้งหมด ดังนั้นอย่าคิดว่ารหัส M2 ทั้งหมดได้รับการพอร์ตอย่างสมบูรณ์แบบ (น่าเสียดายที่) เพียงเพราะคุณพบบางสิ่งในฐานรหัส M2 นั่นไม่ได้หมายความว่า "เป็นวิธีที่ดีที่สุดในการทำ" บางครั้งมันก็แค่ "เรายังไม่ได้แก้ไขมัน"

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

เพียงจำคำแนะนำการเลี้ยงดูที่ดี: "เด็ก ๆ ทำในสิ่งที่ฉันพูดไม่ใช่สิ่งที่ฉันทำ!"


9
คำพูดที่ยอดเยี่ยม: เด็ก ๆ ทำในสิ่งที่ฉันพูดไม่ใช่สิ่งที่ฉันทำ!
sivakumar

นั่นไม่ใช่วิธีการทำงาน kiddo
Ansyori

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

50

\Magento\Framework\App\ObjectManager::getInstance()คุณไม่ควรใช้
มันเอาชนะวัตถุประสงค์ของการฉีดพึ่งพา Mage::getModel()พวกเรากลับมาที่
ผู้จัดการวัตถุควรใช้เฉพาะในโรงงานและจากนั้นฉีดเข้าไปในตัวสร้าง

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


5
ดังนั้นเราทั้งสองตกลงว่ารหัสวีโอไอพีกำลังทำผิดใช่ไหม
กราฟิลส์ที่ Pianism ดิจิตอล

11
ขวา. พวกเขาคิดผิด :)
Marius

ฉันไม่คิดว่าพวกเขาใช้ผิด พวกเขากำลังใช้มันเมื่อจำเป็น: เมื่อต้องการการแก้ไขแบบไดนามิก (โดยเฉพาะอย่างยิ่งปลั๊กอิน) และเมื่อเก็บ BC ในวิธีการที่เลิกใช้ทันที
nevvermind

2
@nevvermind การใช้โรงงาน คุณใช้di.xmlเพื่อสร้าง key => ชื่อคลาสแผนที่และแทรกแผนที่นั้นลงใน Constructor ของโรงงานและใช้โรงงานเพื่อสร้างอินสแตนซ์ของคลาสผ่าน objectmanager
Marius

2
@nevvermind แต่ความเห็นของพนักงานวีโอไอพีมีความสำคัญมากกว่าความคิดเห็นของคุณ คุณมีคำตอบข้างต้นจาก KAndy ที่ระบุด้วยตัวอักษรหนา "คุณไม่ควรใช้ตัวจัดการวัตถุโดยตรง": magento.stackexchange.com/a/117103/146ฉันเดาว่าประเภทของหมอกจะทำให้เกิดปัญหา
Marius

22

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

ฉันไม่รู้ว่าเรื่องทั้งหมดนี่คือการเดาของฉัน:

ในระหว่างการพัฒนา M2 ทีมวีโอไอพีที่บางขั้นตอนวิ่งสคริปต์อัตโนมัติซึ่งแทนที่การเกิดขึ้นของMage:getModel(), Mage::getSingleton(), $layout->createBlock()และอื่น ๆ เพื่อใช้ ObjectManager

การรีฟอร์เรชั่นในภายหลังควรแก้ไขสิ่งนี้ให้ใช้การฉีดแบบพึ่งพาที่เหมาะสม แต่ไม่มีเวลา / ทรัพยากรเพียงพอที่จะแปลงการเกิดขึ้นทั้งหมด

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

อะไรคือผลโดยตรงของการใช้ ObjectManager โดยตรง?

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


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

12

ไม่ควรใช้ Object manager โดยตรง!

ตัวอย่างเช่น

\Magento\Framework\App\ObjectManager::getInstance();

หากคุณกำลังทำงานกับผู้สังเกตการณ์เหตุการณ์หรือปลั๊กอินคุณไม่ควรใช้มันโดยตรง

คุณสามารถใช้มันในโรงงาน แต่ยกเว้นว่าคุณควรฉีดตัวจัดการวัตถุในตัวสร้างก่อนจากนั้นคุณสามารถใช้วัตถุในวิธีการของคุณ

ที่ต้องการใช้:

1) ประกาศวัตถุส่วนตัว:

private $_objectManager;

2) ฉีดในตัวสร้างและเริ่มต้น:

public function __construct(
    \Magento\Framework\ObjectManagerInterface $objectmanager
) {
    $this->_objectManager = $objectmanager;
}

3) ใช้ในวิธีการบางอย่าง:

public function create() {
    return $this->_objectManager->create(/* ......... */);
}

คำตอบนี้มีไว้สำหรับ Magento 2.2 เวอร์ชั่นต่ำกว่าดังนั้นโปรดรับทราบ ตามมาตรฐานใหม่ของวีโอไอพี 2 ตอนนี้เราไม่สามารถใช้งานอินสแตนซ์ objectManager ได้เช่นกัน เราต้องใช้ factory ของ object class หรือ repository เพื่อรับข้อมูลใด ๆ


เป็นวิธีที่ดีที่จะใช้วิธีนี้หรือไม่?
enrico69

ใช่เพราะวีโอไอพีไม่อนุญาตให้ใช้ object โดยตรงผู้จัดการดังนั้นคุณต้องใช้วิธีนี้!
Ronak Chauhan

คุณไม่ควรใช้มันในเหตุการณ์ (ฉันเดาว่าคุณหมายถึงผู้สังเกตการณ์) และปลั๊กอิน คุณควรฉีดวัตถุที่คุณต้องการไม่ใช่ ObjectManager เฉพาะในโรงงานคุณสามารถใช้ ObjectManager แล้วคุณควรฉีดมันแทนการโทร::getInstance()
7ochem

ขวาแก้ไขคำตอบ @ 7ochem
Ronak Chauhan

downvote คำตอบใด ๆ ที่ไม่เหมาะสมหากคุณมีความรู้ที่ดีกว่าคุณสามารถเพิ่มคำตอบของคุณเองหรือคุณสามารถแก้ไขคำตอบของผู้อื่นเพื่อให้ได้แนวคิดที่ดีขึ้นและเป็นประโยชน์ต่อผู้อื่น @ 7ochem
Ronak Chauhan

10

สาเหตุหลักที่ทำให้ผู้พัฒนาไม่สนับสนุนอย่างยิ่งจากการใช้ Object Manager โดยตรงเนื่องจากการใช้ Object Manager โดยตรงทำให้ส่วนขยายไม่สามารถติดตั้งได้ในโหมดรีลีสที่คอมไพล์

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

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

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


2

คุณสามารถลองโดยการสร้างวัตถุของ objectManager และไม่ควรใช้ objectManager โดยตรง

ใช้สิ่งที่ชอบ

class Example extends \Magento\Framework\View\Element\Template
{
    private $_objectManager;

    public function __construct(
        \Magento\Framework\ObjectManagerInterface $objectmanager
    ){
        $this->_objectManager = $objectmanager;
    }

    public function getExample()
    {
        $customerSession = $this->_objectManager->create("Magento\Customer\Model\Session");
        if ($customerSession->isLoggedIn()) {
            $customerData = $customerSession->getCustomer()->getData();
            /*Your logic*/
        }
    }
}

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