Magento 2: giải thích thực tế về lớp proxy là gì?


17

Vì vậy, về mặt lý thuyết, tôi biết thế nào là một lớp proxy trong Magento 2. Tôi đã đọc bài viết tuyệt vời của Alan Storm về nó và tôi hoàn toàn hiểu cách các lớp đó được tạo ra.

Tuy nhiên, và tôi không biết có phải vì tôi là người không nói tiếng Anh bản địa hay nếu lời giải thích của Alan đang sử dụng các lớp không cốt lõi rất trừu tượng, nhưng tôi gặp khó khăn trong việc hiểu cách thức hoạt động và đặc biệt khi sử dụng nó trong quá trình phát triển.

Vì vậy, hãy lấy ví dụ này từ cốt lõi trong 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>

Tôi muốn biết:

  • Tại sao một lớp proxy được sử dụng trong trường hợp cụ thể đó?
  • Khi nào, nói chung, khi nào nên sử dụng một lớp proxy?

Câu trả lời:


17

Cách sử dụng cụ thể này không phải là một ví dụ hay về việc sử dụng mẫu Proxy. Tôi nghĩ rằng nó thậm chí vô dụng trong đoạn mã cụ thể đó, vì một bộ sưu tập không thực hiện bất kỳ hoạt động DB nào trừ khi phương thức tải được gọi. Nếu người quan sát của họ sẽ được sử dụng trong lớp lệnh console như là phần phụ thuộc, thì việc sử dụng proxy là hợp lý.

Lớp proxy chỉ nên được sử dụng khi trong quá trình xây dựng đối tượng bạn thực hiện một thao tác đắt tiền. Một ví dụ điển hình là các lệnh của bảng điều khiển Symfony:

Hãy tưởng tượng lệnh console của bạn đang sử dụng ProductRep repository làm phụ thuộc. Trình xây dựng kho lưu trữ sản phẩm thiết lập kết nối MySQL với cơ sở dữ liệu danh mục.

Nó có nghĩa là trên mỗi bin/magentocuộc gọi, bất kể bạn thực hiện lệnh nào, các phụ thuộc kho lưu trữ sẽ được khởi tạo. Vì vậy, cách duy nhất để tránh nó là sử dụng ngay lập tức lười biếng đối tượng ban đầu bằng cách tạo proxy. Trong trường hợp cơ sở dữ liệu này, kết nối với cơ sở dữ liệu danh mục sẽ chỉ được thiết lập khi bạn gọi một phương thức lưu trữ.

Hy vọng rằng sẽ giúp hiểu ý tưởng của proxy tốt hơn.


1
Việc ví dụ tôi chọn là vô ích khiến tôi càng bối rối hơn. Một lần nữa về mặt lý thuyết tôi hiểu khái niệm. Nhưng những gì tôi không nhận được: tại sao bạn lại thêm ProductRep repository làm phụ thuộc vào lệnh console nếu bạn không sử dụng nó cho mọi lệnh. Không phải nó chỉ là một phụ thuộc cho các lệnh bạn sử dụng sao? Theo những gì bạn nói, Proxy là một cách để "bỏ qua" một phụ thuộc? Nhưng trong trường hợp đó, tại sao đó là một sự phụ thuộc ở nơi đầu tiên?
Raphael tại Nghệ thuật piano kỹ thuật số

1
Tôi nghĩ lệnh điều khiển Symfony là một ví dụ tuyệt vời, vì bạn phải nói chuyện với Magento từ đó và cách duy nhất để làm điều đó là chỉ định một phụ thuộc trong hàm tạo. Trong thành phần bảng điều khiển Symfony, bạn phải tạo một lớp cho mỗi lệnh đơn. Lớp này đã cấu hình và thực hiện các phương thức. Cấu hình đặt tên và đối số của nó, trong khi thực thi thực sự là thực thi thao tác đắt tiền. Nếu thao tác đắt tiền được thực hiện trên cấu hình, hơn là bạn bị lừa, đó là lý do tại sao proxy là câu trả lời cho vấn đề này.
Ivan Chepurnyi

13

Một lớp proxy cho phép bạn phụ thuộc vào một lớp mà bạn không nhất thiết cần và điều đó có chi phí cao liên quan đến việc đó.

Nếu bạn nhìn vào một proxy Magento đã tạo, như thế \Magento\Framework\View\Layout\Proxy, bạn sẽ thấy nó có tất cả các phương thức giống như lớp gốc. Sự khác biệt là mỗi khi bất kỳ ai trong số chúng được gọi, nó sẽ kiểm tra xem lớp đó có phải là proxy thực sự đã được khởi tạo hay không và tạo đối tượng nếu không. (Điều này xảy ra trong một _getSubject()hoặc _getCache()phương thức.)

Đó là tải lười biếng cho tiêm phụ thuộc.

Bạn nên sử dụng proxy nếu lớp phụ thuộc không phải lúc nào cũng được lớp của bạn sử dụng và:

  • Có rất nhiều phụ thuộc của riêng nó, hoặc
  • Hàm tạo của nó liên quan đến mã chuyên sâu tài nguyên, hoặc
  • Tiêm nó có tác dụng phụ

Một ví dụ điển hình của việc này là các phiên. Nhận phiên thông qua ObjectManager là một cách làm không tốt, nhưng việc tiêm một lớp phiên như \Magento\Customer\Model\Sessioncó thể phá vỡ mọi thứ nếu lớp của bạn chạy bên ngoài phạm vi của phiên đó (giả sử bạn tiêm phiên khách hàng frontend trên trang quản trị). \Magento\Customer\Model\Session\ProxyThay vào đó, bạn khắc phục điều đó bằng cách tiêm proxy của phiên và chỉ tham chiếu nó khi bạn biết nó hợp lệ. Trừ khi bạn tham chiếu nó, phiên không bao giờ được khởi tạo và không có gì phá vỡ.

Trong ví dụ cụ thể của bạn di.xml, có vẻ như họ đã sử dụng proxy để biện minh cho việc tiêm bộ điều khiển thay vì nhà máy của bộ điều khiển đó. Dù bằng cách nào, đó không phải là những gì proxy được dự định sẽ được sử dụng và lợi ích của nó trong tình huống đó có thể là tối thiểu.


7

Magento 2 loại proxy tự phát có thể được sử dụng để "sửa chữa" các lỗi thiết kế. Điều đó có thể rất tiện dụng. Có 2 trường hợp sử dụng:

  1. Bao bọc một đồ thị đối tượng đắt tiền mà người phụ thuộc có thể không cần thiết mỗi lần.

  2. Phá vỡ một phụ thuộc theo chu kỳ trong đó lớp Aphụ thuộc Bvà lớp Bphụ thuộc vào A.
    Tiêm B\Proxyvào Acho phép bạn khởi tạo A, sau đó lần lượt có thể được sử dụng để khởi tạo Bkhi nó thực sự được sử dụng với Ađối tượng thực .

Trong trường hợp 1. sự phụ thuộc không luôn luôn được sử dụng là một dấu hiệu cho thấy lớp người phụ thuộc thực hiện nhiều hoặc có thể làm nhiều bằng một phương thức. Lệnh console @ivan được đề cập là một ví dụ tốt về điều đó.

Trong trường hợp 2. Tôi không biết một cách chung để phá vỡ sự phụ thuộc đó. Tôi có xu hướng viết lại nếu có thời gian, nhưng đó có thể không phải là một lựa chọn.

Cũng như một ghi chú bên lề, tôi muốn nói thêm rằng có nhiều loại proxy trong OOP hơn là khởi tạo lười biếng được tạo tự động mà Magento 2 sử dụng (ví dụ: proxy từ xa).


Xin chào @ vinai, cách sử dụng các lớp proxy thông qua phương thức __constructor () hoặc qua di.xml là gì?
akgola

1
Theo hướng dẫn mã hóa Magento, phần 2.5 proxy KHÔNG được khai báo trong các hàm tạo của lớp. Proxy phải được khai báo trong di.xml. Xem devdocs.magento.com/guides/v2.3/coding-stiterias/ trên
Vinai

1

Đây là những câu trả lời

Tại sao một lớp proxy được sử dụng trong trường hợp cụ thể đó?

Nếu bạn xem kỹ mã bên dưới được viết cho lớp "SetConversionValueObserver", nếu Google adwards không hoạt động "return" và nếu không có lệnh "return". Phương tiện, Đối tượng thu thập đơn hàng sẽ chỉ được tạo khi Id đơn hàng tồn tại và Google adwords hoạt động. nếu chúng ta chèn lớp bộ sưu tập Đơn hàng thực tế thì trình quản lý đối tượng tạo đối tượng bộ sưu tập với các đối tượng lớp cha của nó mà không biết Google adwords không hoạt động và điều đó làm chậm trang thành công của đơn hàng. vì vậy, tốt hơn nên tạo đối tượng theo yêu cầu đó là sử dụng proxy. /vendor/magento/module-google-AHA/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;
}

Khi nào, nói chung, khi nào nên sử dụng một lớp proxy? - Tiêm lớp Proxy khi bạn cảm thấy việc tạo đối tượng sẽ tốn kém và hàm tạo của lớp đặc biệt tốn nhiều tài nguyên. - khi bạn không muốn tác động hiệu suất không cần thiết do tạo đối tượng. - khi bạn cảm thấy việc tạo đối tượng sẽ xảy ra khi bạn gọi phương thức cụ thể trong điều kiện cụ thể không phải lúc nào cũng. Ví dụ: Trình xây dựng bố cục là một tài nguyên chuyên sâu.

Trình tạo bố cục thực tế so với layout / proxy

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;
}

Trình xây dựng proxy, hãy xem, không có hàm tạo cha mẹ nào được gọi cũng như chỉ truyền tên lớp bố cục để việc tạo đối tượng thực tế xảy ra khi phương thức được gọi.

 /**
 * 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;
}

Lớp proxy có phương thức để tạo đối tượng theo yêu cầu, _subject là đối tượng của lớp đã qua.

/**
 * 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;
}

Và phương thức được gọi là sử dụng _subject.

/**
 * {@inheritdoc}
 */
public function setGeneratorPool(\Magento\Framework\View\Layout\GeneratorPool $generatorPool)
{
    return $this->_getSubject()->setGeneratorPool($generatorPool);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.