Magento 2: Proxy sınıfı nedir pratik açıklama?


17

Yani, teorik olarak Magento 2'de bir proxy sınıfının ne olduğunu biliyorum. Bu konuda harika Alan Storm makalesini okudum ve bu sınıfların nasıl oluşturulduğunu tamamen anlıyorum.

Ancak, bunun ana dili İngilizce olmayan biri olduğumu veya Alan'ın açıklamalarının çok soyut olan çekirdek olmayan sınıfları kullanıp kullanmadığını bilmiyorum, ancak nasıl çalıştığını ve özellikle ne zaman kullanılacağını anlamakta zorlanıyorum geliştirme sırasında.

Öyleyse bu örneği çekirdekten alalım 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>

Bilmek isterim:

  • bu durumda neden bir vekil sınıf kullanılır ?
  • genel olarak ne zaman bir proxy sınıfı kullanılmalıdır?

Yanıtlar:


18

Bu özel kullanım Proxy kalıbı kullanımına iyi bir örnek değildir. Yük yöntemi çağrılmadığı sürece bir koleksiyon herhangi bir DB işlemleri yapmıyor gibi, bu kod parçası bile işe yaramaz olduğunu düşünüyorum. Gözlemcileri konsol komut sınıfında bağımlılık olarak kullanılacaksa, proxy kullanmak mantıklıdır.

Proxy sınıfı yalnızca nesnenin inşası sırasında pahalı bir işlem yürütürken kullanılmalıdır. İyi bir örnek Symfony konsol komutlarıdır:

Konsol komutunuzun ProductRepository'yi bağımlılık olarak kullandığını düşünün. Ürün veri havuzu kurucusu, katalog veritabanına MySQL bağlantısı kurar.

Her bin/magentoçağrıda, hangi komutu uygularsanız uygulansın, depo bağımlılıkları somutlaştırılacaktır. Bundan kaçınmanın tek yolu, bir proxy oluşturarak orijinal nesnenin tembel örneklemesini kullanmaktır. Bu durumda veritabanı, katalog veri tabanına bağlantı yalnızca bir depo yöntemini çağırdığınızda kurulacaktır.

Umarım proxy fikrini daha iyi anlayabiliriz.


1
Seçtiğim örneğin işe yaramaz olması beni daha da karıştırdı. Yine teorik olarak kavramı anlıyorum. Ama ne elde değil: neden her komut için kullanmıyorsanız ProductRepository konsol komutuna bağımlılık olarak eklemek istiyorsunuz. Yalnızca kullandığınız komutlara bağımlılık olmamalı mı? Söylediklerine göre, Proxy bir bağımlılığı "atlamanın" bir yoludur? Fakat bu durumda, neden bu ilk etapta bir bağımlılıktır?
Raphael, Dijital Piyanizm'de

1
Sanırım Symfony konsol komutu harika bir örnek, ondan Magento ile konuşmanız gerekiyor ve bunu yapmanın tek yolu yapıcıda bir bağımlılık belirtmektir. Symfony konsol bileşeninde her komut için bir sınıf oluşturmanız gerekir. Bu sınıfın yapılandırma ve yürütme yöntemleri vardır. Configure aslında pahalı bir işlem yürütürken Configure adını ve bağımsız değişkenlerini ayarlar. Config'de pahalı işlem yapılıyorsa, vidalandığınızdan daha çok proxy'nin nedeni bu sorunun cevabıdır.
Ivan Chepurnyi

13

Proxy sınıfı, zorunlu olarak ihtiyacınız olmayacak ve bununla ilişkili yüksek bir maliyeti olan bir sınıfa bağımlılık enjekte etmenizi sağlar.

Magento'nun oluşturduğu bir proxy'ye bakarsanız \Magento\Framework\View\Layout\Proxy, orijinal sınıfla aynı yöntemlerin hepsine sahip olduğunu göreceksiniz. Fark, bunlardan her çağrıldığında, proxy olduğu sınıfın gerçekten başlatılıp başlatılmadığını kontrol etmesi ve nesneyi oluşturmamasıdır. (Bu bir _getSubject()veya _getCache()yöntemde olur .)

Bağımlılık enjeksiyonu için tembel yükleme.

Sınıf bağımlılığı her zaman sınıfınız tarafından kullanılmıyorsa bir proxy kullanmalısınız ve:

  • Çok fazla bağımlılığı var, veya
  • Yapıcısı, kaynak yoğun kod içerir veya
  • Enjekte etmenin yan etkileri vardır

Buna iyi bir örnek oturumlardır. ObjectManager aracılığıyla oturum almak kötü bir uygulamadır, ancak sınıfınız \Magento\Customer\Model\Sessiono oturumun kapsamı dışında çalışırsa (örneğin, ön uç müşteri oturumunu bir yönetici sayfasına enjekte ettiğiniz gibi) bir oturum sınıfının enjekte edilmesi işleri bozabilir. Bunun \Magento\Customer\Model\Session\Proxyyerine oturumun proxy'sini enjekte ederek ve yalnızca geçerli olduğunu bildiğinizde başvurarak bu sorunu çözersiniz. Siz referans vermedikçe, oturum asla somutlaştırılmaz ve hiçbir şey kesilmez.

Özel örneğinizde di.xml, proxy'yi o denetleyicinin fabrikası yerine bir denetleyiciyi enjekte etmek için kullandıkları anlaşılıyor. Her iki durumda da, vekillerin kullanılması amaçlanan bu değildir ve bu durumda bunun yararı muhtemelen minimumdur.


7

Magento 2 tipi otomatik oluşturulmuş proxy'ler tasarım hatalarını "düzeltmek" için kullanılabilir. Bu çok kullanışlı olabilir. 2 kullanım durumu vardır:

  1. Dependee tarafından her seferinde gerekli olmayabilecek pahalı bir nesne grafiği sarın.

  2. Sınıfın Abağlı olduğu Bve sınıfın Bbir olduğu bir döngüsel bağımlılığı kırın A.
    Enjekte B\Proxyiçine Aörneğini yapmanızı sağlar Asonra sırayla örneğini kullanılabilecek, Baslında gerçek ile kullanıldığında Anesne.

1. Durumda, her zaman kullanılmayan bağımlılık, dependee sınıfının çok şey yaptığı ya da belki de bir yöntemle yaptığı bir işarettir. @İvan adlı konsol komutu buna iyi bir örnektir.

Durumunda 2. O bağımlılığı kırmak için genel bir yol bilmiyorum. Zaman varsa yeniden yazma eğilimindeyim ama bu bir seçenek olmayabilir.

Bir yan not olarak, OOP'de, bir Magento 2'nin kullandığı otomatik oluşturulmuş tembel örneklemeden (örn. Uzak proxy) çok daha fazla proxy türü olduğunu eklemek isterim.


Merhaba @ vinai, __constructor () yöntemi veya di.xml aracılığıyla proxy sınıflarını kullanmanın yolu nedir?
akgola

1
Magento kodlama kılavuzları bölümüne göre sınıf kurucularında 2.5 vekil beyan EDİLMEMELİDİR. Vekil sunucuları di.xml dosyasında bildirilmelidir. Bkz. Devdocs.magento.com/guides/v2.3/coding-standards/…
Vinai

1

İşte cevaplar

bu durumda neden bir vekil sınıf kullanılır?

Eğer "SetConversionValueObserver" sınıfı için yazılan kodun altına yakından bakarsanız, Google taraftarları aktif "return" değilse ve "return" emri yoksa. Araçlar, Sipariş Toplama Nesnesi yalnızca sipariş Kimlikleri varsa ve Google adwords etkin olduğunda oluşturulur. gerçek Sipariş toplama sınıfını enjekte edersek, nesne yöneticisi Google adwords’ünün etkin olmadığını ve sipariş başarı sayfasını yavaşlattığını bilmeden üst sınıf nesneleriyle toplama nesnesi oluşturur. yani, istek üzerine proxy kullanımı daha iyi nesne oluşturmak. /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;
}

genel olarak ne zaman bir proxy sınıfı kullanılmalıdır? - Nesne oluşturmanın pahalı olacağını ve sınıfın yapıcısının özellikle kaynak yoğun olduğunu düşünüyorsanız Proxy sınıfını enjekte edin. - nesne oluşturma nedeniyle gereksiz performans etkisi istemediğinizde. - Belirli bir yöntemi her zaman değil, belirli bir yöntem çağırdığınızda nesne oluşturma gerektiğini hissettiğinizde. Örneğin, Düzen yapıcısı kaynak yoğun bir iştir.

Gerçek Mizanpaj yapıcısı vs düzen / 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 kurucu, bir göz atın, hiçbir üst kurucu çağrıldı yanı sıra sadece düzeni sınıfı adı geçti böylece gerçek nesne oluşturma yöntemi çağrıldığında gerçekleşir.

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

Proxy sınıfının istek üzerine nesne oluşturma yöntemi vardır, _subject geçirilen sınıfın nesnesidir.

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

Ve yöntemi _subject kullanarak çağırdı.

/**
 * {@inheritdoc}
 */
public function setGeneratorPool(\Magento\Framework\View\Layout\GeneratorPool $generatorPool)
{
    return $this->_getSubject()->setGeneratorPool($generatorPool);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.