Magento 2: คำอธิบายการใช้งานจริงของคลาส proxy คืออะไร?


17

ดังนั้นฉันจึงรู้ทางทฤษฎีว่าคลาสพร็อกซีใน Magento 2 คืออะไรฉันได้อ่านบทความ Alan Storm ที่ยอดเยี่ยมเกี่ยวกับเรื่องนี้และฉันเข้าใจว่าการเรียนเหล่านั้นเกิดขึ้นได้อย่างไร

อย่างไรก็ตามและฉันไม่รู้ว่าเป็นเพราะฉันไม่ใช่เจ้าของภาษาอังกฤษหรือคำอธิบายของอลันกำลังใช้ชั้นเรียนที่ไม่ใช่แกนหลักซึ่งเป็นนามธรรมมาก แต่ฉันมีเวลายากที่จะเข้าใจว่ามันใช้งานได้อย่างไรและพิเศษเมื่อใช้ ระหว่างการพัฒนา

ลองทำตัวอย่างจากแกนในapp/code/Magento/GoogleAdwords/etc/di.xml:

<?xml version="1.0"?>
<!--
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\GoogleAdwords\Observer\SetConversionValueObserver">
        <arguments>
            <argument name="collection" xsi:type="object">Magento\Sales\Model\ResourceModel\Order\Collection\Proxy</argument>
        </arguments>
    </type>
</config>

ผมอยากจะรู้ว่า:

  • เหตุใดจึงใช้คลาสพร็อกซีในกรณีพิเศษนั้น
  • โดยทั่วไปเมื่อใดควรใช้คลาสพร็อกซี

คำตอบ:


17

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

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

ลองนึกภาพคำสั่งคอนโซลของคุณกำลังใช้ ProductRepository เป็นการอ้างอิง ตัวสร้างพื้นที่เก็บข้อมูลผลิตภัณฑ์สร้างการเชื่อมต่อ MySQL กับฐานข้อมูลแคตตาล็อก

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

หวังว่าจะช่วยให้เข้าใจแนวคิดของพร็อกซีได้ดีขึ้น


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

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

13

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

หากคุณดูพร็อกซีที่วีโอไอพีสร้างขึ้น\Magento\Framework\View\Layout\Proxyคุณจะเห็นว่ามันมีวิธีการเดียวกับคลาสเดิมทั้งหมด ความแตกต่างคือทุกครั้งที่มีการเรียกสิ่งเหล่านั้นมันจะตรวจสอบว่าคลาสที่เป็นพร็อกซีได้ถูกสร้างขึ้นจริงหรือไม่และสร้างวัตถุถ้าไม่ (สิ่งนี้เกิดขึ้นใน_getSubject()หรือ_getCache()วิธีการ)

มันขี้เกียจโหลดสำหรับการฉีดพึ่งพา

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

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

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

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


7

พร็อกซีที่สร้างอัตโนมัติชนิดวีโอไอพี 2 สามารถใช้เพื่อ "แก้ไข" ข้อผิดพลาดในการออกแบบ มันมีประโยชน์มาก มี 2 ​​กรณีใช้งาน:

  1. ห่อกราฟวัตถุราคาแพงที่อาจไม่จำเป็นต้องใช้ทุกครั้งโดยผู้ที่ต้องพึ่งพา

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

ในกรณีที่ 1.การขึ้นต่อกันที่ไม่ได้ใช้เสมอเป็นสัญญาณที่คลาส Depee ทำมากหรืออาจทำได้หลายวิธี คอนโซลคำสั่ง @ivan ที่กล่าวถึงเป็นตัวอย่างที่ดีของสิ่งนั้น

ในกรณีที่2ฉันไม่รู้วิธีทั่วไปในการแยกการพึ่งพานั้น ฉันมักจะเขียนใหม่หากมีเวลา แต่นั่นอาจไม่ใช่ตัวเลือก

เช่นเดียวกับบันทึกด้านข้างฉันต้องการเพิ่มว่ามีหลายประเภทพร็อกซีใน OOP มากกว่าการสร้างอินสแตนซ์ขี้เกียจอัตโนมัติที่ Magento 2 ใช้ (เช่นพร็อกซีระยะไกล)


Hello @ vinai วิธีใช้พร็อกซีคลาสผ่านวิธี __constructor () หรือผ่าน di.xml เป็นอย่างไร
akgola

1
ตามหลักเกณฑ์การเข้ารหัสของวีโอไอพีตอนที่ 2.5 ผู้รับมอบฉันทะต้องไม่ประกาศในตัวสร้างคลาส พร็อกซีต้องประกาศใน di.xml ดูdevdocs.magento.com/guides/v2.3/coding-standards/…
Vinai

1

นี่คือคำตอบ

เหตุใดจึงใช้คลาสพร็อกซีในกรณีพิเศษนั้น

หากคุณดูโค้ดด้านล่างซึ่งเขียนขึ้นสำหรับคลาส "SetConversionValueObserver" หาก Google adwards ไม่ได้ใช้งาน "ส่งคืน" และหากไม่มีคำสั่ง "ส่งคืน" หมายความว่าวัตถุชุดคำสั่งซื้อจะถูกสร้างขึ้นเฉพาะเมื่อมีรหัสคำสั่งซื้อและ Google adwords ทำงานอยู่ หากเราฉีดคลาสคอลเลกชันการสั่งซื้อจริงแล้วผู้จัดการวัตถุสร้างวัตถุคอลเลกชันด้วยวัตถุคลาสแม่โดยไม่ทราบว่า Google adwords ไม่ได้ใช้งานและที่ประสบความสำเร็จในการสั่งซื้อหน้าช้าลง ดังนั้นควรสร้างออบเจ็กต์ตามต้องการที่ใช้พร็อกซีให้ดีขึ้น /vendor/magento/module-google-adwords/Observer/SetConversionValueObserver.php

 /**
 * Set base grand total of order to registry
 *
 * @param \Magento\Framework\Event\Observer $observer
 * @return \Magento\GoogleAdwords\Observer\SetConversionValueObserver
 */
public function execute(\Magento\Framework\Event\Observer $observer)
{
    if (!($this->_helper->isGoogleAdwordsActive() && $this->_helper->isDynamicConversionValue())) {
        return $this;
    }
    $orderIds = $observer->getEvent()->getOrderIds();
    if (!$orderIds || !is_array($orderIds)) {
        return $this;
    }
    $this->_collection->addFieldToFilter('entity_id', ['in' => $orderIds]);
    $conversionValue = 0;
    /** @var $order \Magento\Sales\Model\Order */
    foreach ($this->_collection as $order) {
        $conversionValue += $order->getBaseGrandTotal();
    }
    $this->_registry->register(
        \Magento\GoogleAdwords\Helper\Data::CONVERSION_VALUE_REGISTRY_NAME,
        $conversionValue
    );
    return $this;
}

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

ตัวสร้างเค้าโครงที่แท้จริงเทียบกับรูปแบบ / พร็อกซี

public function __construct(
    Layout\ProcessorFactory $processorFactory,
    ManagerInterface $eventManager,
    Layout\Data\Structure $structure,
    MessageManagerInterface $messageManager,
    Design\Theme\ResolverInterface $themeResolver,
    Layout\ReaderPool $readerPool,
    Layout\GeneratorPool $generatorPool,
    FrontendInterface $cache,
    Layout\Reader\ContextFactory $readerContextFactory,
    Layout\Generator\ContextFactory $generatorContextFactory,
    AppState $appState,
    Logger $logger,
    $cacheable = true,
    SerializerInterface $serializer = null
) {
    $this->_elementClass = \Magento\Framework\View\Layout\Element::class;
    $this->_renderingOutput = new \Magento\Framework\DataObject();
    $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class);

    $this->_processorFactory = $processorFactory;
    $this->_eventManager = $eventManager;
    $this->structure = $structure;
    $this->messageManager = $messageManager;
    $this->themeResolver = $themeResolver;
    $this->readerPool = $readerPool;
    $this->generatorPool = $generatorPool;
    $this->cacheable = $cacheable;
    $this->cache = $cache;
    $this->readerContextFactory = $readerContextFactory;
    $this->generatorContextFactory = $generatorContextFactory;
    $this->appState = $appState;
    $this->logger = $logger;
}

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

 /**
 * Proxy constructor
 *
 * @param \Magento\Framework\ObjectManagerInterface $objectManager
 * @param string $instanceName
 * @param bool $shared
 */
public function __construct(
    \Magento\Framework\ObjectManagerInterface $objectManager,
    $instanceName = \Magento\Framework\View\Layout::class,
    $shared = true
) {
    $this->_objectManager = $objectManager;
    $this->_instanceName = $instanceName;
    $this->_isShared = $shared;
}

คลาสพร็อกซีมีวิธีสร้างวัตถุตามต้องการ _subject เป็นวัตถุของคลาสที่ส่งผ่าน

/**
 * Get proxied instance
 *
 * @return \Magento\Framework\View\Layout
 */
protected function _getSubject()
{
    if (!$this->_subject) {
        $this->_subject = true === $this->_isShared
            ? $this->_objectManager->get($this->_instanceName)
            : $this->_objectManager->create($this->_instanceName);
    }
    return $this->_subject;
}

และวิธีการที่เรียกว่าใช้ _subject

/**
 * {@inheritdoc}
 */
public function setGeneratorPool(\Magento\Framework\View\Layout\GeneratorPool $generatorPool)
{
    return $this->_getSubject()->setGeneratorPool($generatorPool);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.