Magento 2: penjelasan praktis tentang apa itu kelas proxy?

17

Jadi, saya tahu secara teoritis apa yang dimaksud dengan kelas proxy di Magento 2. Saya telah membaca artikel Alan Storm yang luar biasa tentang hal itu dan saya benar-benar mengerti bagaimana kelas-kelas itu dihasilkan.

Namun, dan saya tidak tahu apakah itu karena saya bukan penutur bahasa Inggris asli atau jika penjelasan Alan menggunakan kelas non-inti yang sangat abstrak, tapi saya kesulitan memahami cara kerjanya dan khususnya kapan harus menggunakan itu selama pengembangan.

Jadi mari kita ambil contoh ini dari inti di 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>

Saya ingin tahu:

  • mengapa kelas proxy digunakan dalam kasus khusus itu?
  • kapan, secara umum, haruskah seseorang menggunakan kelas proxy?
Raphael di Digital Pianism
sumber

Jawaban:

17

Penggunaan khusus ini bukan contoh yang baik untuk menggunakan pola Proxy. Saya pikir itu bahkan tidak berguna dalam potongan kode tertentu, karena kumpulan tidak melakukan operasi DB kecuali metode pemuatan disebut. Jika pengamat mereka akan digunakan dalam kelas perintah konsol sebagai dependensi, maka masuk akal untuk menggunakan proxy.

Kelas proxy seharusnya hanya digunakan ketika selama konstruksi objek yang Anda jalankan operasi mahal. Contoh yang baik adalah perintah konsol Symfony:

Bayangkan perintah konsol Anda menggunakan ProductRepository sebagai ketergantungan. Pembuat repositori produk membuat koneksi MySQL ke database katalog.

Ini berarti pada setiap bin/magentopanggilan, tidak peduli perintah mana yang Anda jalankan, dependensi repositori akan dipakai. Jadi satu-satunya cara untuk menghindarinya adalah dengan menggunakan instantiation malas dari objek asli dengan membuat proxy. Dalam hal ini basis data, koneksi ke basis data katalog akan dibuat hanya ketika Anda memanggil metode repositori.

Harapan yang membantu untuk memahami gagasan proxy dengan lebih baik.

Ivan Chepurnyi
sumber
1
Fakta bahwa contoh yang saya pilih tidak berguna membuat saya semakin bingung. Lagi secara teori saya mengerti konsepnya. Tapi yang tidak saya dapatkan: mengapa Anda menambahkan ProductRepository sebagai ketergantungan pada perintah konsol jika Anda tidak menggunakannya untuk setiap perintah. Bukankah itu hanya ketergantungan untuk perintah yang Anda gunakan? Menurut apa yang Anda katakan, Proxy adalah cara untuk "melewatkan" ketergantungan? Tetapi dalam kasus itu, mengapa itu menjadi ketergantungan?
Raphael di Digital Pianism
1
Saya pikir perintah konsol Symfony adalah contoh yang bagus, karena Anda harus berbicara dengan Magento darinya, dan satu-satunya cara untuk melakukannya adalah menentukan ketergantungan pada konstruktor. Dalam komponen konsol Symfony, Anda harus membuat kelas untuk setiap perintah. Kelas ini memiliki metode konfigurasi dan eksekusi. Konfigurasikan set nama dan argumennya, sementara eksekusi sebenarnya mengeksekusi operasi yang mahal. Jika operasi mahal dijalankan pada konfigurasi, daripada Anda kacau, itu sebabnya proksi adalah jawaban untuk masalah ini.
Ivan Chepurnyi
13

Kelas proxy memungkinkan Anda ketergantungan-menyuntikkan kelas yang Anda tidak perlu, dan yang memiliki biaya tinggi terkait dengan melakukannya.

Jika Anda melihat proxy yang dibuat Magento, seperti \Magento\Framework\View\Layout\Proxy, Anda akan melihatnya memiliki semua metode yang sama dengan kelas aslinya. Perbedaannya adalah bahwa setiap kali ada yang dipanggil, ia memeriksa apakah kelas itu adalah proxy sebenarnya telah dipakai, dan membuat objek jika tidak. (Ini terjadi dalam _getSubject()atau _getCache()metode.)

Pemuatan malas untuk injeksi ketergantungan.

Anda harus menggunakan proxy jika ketergantungan kelas tidak selalu digunakan oleh kelas Anda, dan:

  • Memiliki banyak dependensi sendiri, atau
  • Konstruktornya melibatkan kode sumber daya intensif, atau
  • Menyuntikkan itu memiliki efek samping

Salah satu contoh yang baik dari ini adalah sesi. Mendapatkan sesi melalui ObjectManager adalah praktik yang buruk, tetapi menyuntikkan kelas sesi seperti \Magento\Customer\Model\Sessiondapat merusak hal-hal jika kelas Anda pernah berjalan di luar ruang lingkup sesi itu (misalnya Anda menyuntikkan sesi pelanggan frontend pada halaman admin). Anda menyiasatinya dengan menyuntikkan proksi sesi \Magento\Customer\Model\Session\Proxysebagai gantinya, dan hanya merujuk ketika Anda tahu itu valid. Kecuali Anda referensi, sesi tidak pernah dipakai, dan tidak ada yang rusak.

Dalam contoh spesifik Anda di.xml, sepertinya mereka menggunakan proksi untuk membenarkan menyuntikkan pengontrol daripada pabrik pengontrol itu. Either way, bukan itu yang dimaksudkan untuk menggunakan proxy, dan manfaatnya dalam situasi itu kemungkinan minimal.

Ryan Hoerr
sumber
7

Proxy autogenerated tipe Magento 2 dapat digunakan untuk "memperbaiki" kesalahan desain. Itu bisa sangat berguna. Ada 2 kasus penggunaan:

  1. Bungkus grafik objek mahal yang mungkin tidak diperlukan setiap saat oleh tanggungan.

  2. Hancurkan ketergantungan siklik tempat kelas Abergantung Bdan kelas Bbergantung pada A.
    Menyuntikkan B\Proxyke dalam Amemungkinkan Anda instantiate A, yang kemudian dapat digunakan untuk instantiate Bketika itu benar-benar digunakan dengan Aobjek nyata .

Dalam kasus 1. ketergantungan yang tidak selalu digunakan adalah tanda bahwa kelas tanggungan melakukan banyak hal, atau mungkin melakukan banyak hal dengan satu metode. Perintah konsol @ivan yang disebutkan adalah contoh yang bagus.

Dalam kasus 2. Saya tidak tahu cara umum untuk memecah ketergantungan itu. Saya cenderung menulis ulang jika ada waktu, tetapi itu mungkin bukan pilihan.

Sama seperti catatan tambahan, saya ingin menambahkan bahwa ada lebih banyak jenis proxy dalam OOP daripada instantiasi malas autogenerated yang digunakan Magento 2 (mis. Proxy jarak jauh).

Vinai
sumber
Halo @ vinai, apa cara menggunakan kelas proxy melalui metode __constructor () atau melalui di.xml.?
akgola
1
Menurut pedoman pengkodean Magento bagian 2.5 proksi TIDAK HARUS dinyatakan dalam konstruktor kelas. Proxy HARUS dinyatakan dalam di.xml. Lihat devdocs.magento.com/guides/v2.3/coding-standards/…
Vinai
1

Inilah jawabannya

mengapa kelas proxy digunakan dalam kasus khusus itu?

Jika Anda melihat dari dekat kode di bawah ini yang ditulis untuk kelas "SetConversionValueObserver", jika Google adwards tidak aktif "kembali" dan jika tidak ada pesanan "kembali". Berarti, Objek Pengumpulan Pesanan akan dibuat hanya jika Id ada dan Google adwords aktif. jika kita menyuntikkan kelas koleksi pesanan aktual maka manajer objek membuat objek koleksi dengan objek kelas induknya tanpa mengetahui Google adwords tidak aktif dan memperlambat halaman keberhasilan pesanan. jadi, lebih baik buat objek sesuai permintaan yaitu penggunaan proxy. /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;
}

kapan, secara umum, haruskah seseorang menggunakan kelas proxy? - Suntikkan kelas Proxy ketika Anda merasa pembuatan objek akan mahal dan konstruktor kelas sangat membutuhkan sumber daya. - ketika Anda tidak ingin dampak kinerja yang tidak perlu karena pembuatan objek. - ketika Anda merasa penciptaan objek harus terjadi ketika Anda memanggil metode tertentu dalam kondisi tertentu tidak selalu. Misalnya konstruktor Tata Letak adalah sumber daya-intensif.

Konstruktor Tata Letak Aktual vs tata letak / 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;
}

Proxy konstruktor, lihatlah, tidak ada induk konstruktor yang dipanggil serta hanya melewati nama kelas tata letak sehingga penciptaan objek aktual terjadi ketika metode dipanggil.

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

Kelas proxy memiliki metode untuk membuat objek sesuai permintaan, _subjek adalah objek dari kelas yang lulus.

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

Dan metode yang disebut menggunakan _subject.

/**
 * {@inheritdoc}
 */
public function setGeneratorPool(\Magento\Framework\View\Layout\GeneratorPool $generatorPool)
{
    return $this->_getSubject()->setGeneratorPool($generatorPool);
}
MukeshphpMysql
sumber