Magento 2:直接使用还是不使用ObjectManager?


134

好的,昨天我们就与Magento社区的其他人就直接使用ObjectManagerclass / templates进行了大讨论。

我已经知道了我们不应该直接使用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);

而不是经历痛苦但推荐的过程

  • 创建一个模块
  • 宣布偏好
  • 注入依赖
  • 声明一个公共方法

但是,随之而来的难题是,Magento 2核心文件经常直接调用ObjectManager。可以在此处找到一个简单的示例:https : //github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

所以这是我的问题:

  • Magento为什么要做他们建议我们不要做的事情?这是否意味着在某些情况下我们应该ObjectManager直接使用Direct?如果是这样,那是什么情况?
  • 直接使用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
当OM已经存在时,有一段M2开发的时间,但是整个magento尚未更改为使用构造函数注入。那时,许多人用ObjectManager :: getInstance()-> get()替换了Mage :: getSingleton()。大多数此类用法是在那个时期引入的。后来,所有Mage :: getSingleton()调用都被工具替换为构造函数注入,但是该工具无法识别ObjectManager :: getInstance(),因此没有将其替换为构造函数注入。
安东·克里尔


3
@TejabhagavanKollepara你读过两个问题吗?有相似之处,但彼此之间没有重复之处
拉斐尔(Raphael)在Digital Pianism上

Answers:


98

您不应该直接使用ObjectManager!

该规则的例外是:

  • 在静态魔术方法一样 __wakeupserialize
  • 万一你应该向后兼容构造函数
  • 在全球范围内,例如在集成测试中。
  • 在只需要创建工厂,代理等对象的类中

2
我知道我永远都不要直接使用它,但是Magento为什么要这样做?^^
拉斐尔(Raphael)在Digital Pianism上,2013年

2
您的示例中是为了向后兼容
KAndy

那些总是被标记为@deprecated吗?
拉斐尔(Raphael)在


5
哦,是的,我知道这很令人困惑。也许他们应该说“不做,但是要知道我们可能在这里和那里留下了一些错误”;)
拉斐尔在Digital Pianism上2016年

53

那么为什么我们建议反对时M2有时有时直接访问对象管理器?

残酷的回答:M2是M1的端口-不是完全重写。因此,不要以为所有M2代码都已完美移植(不幸的是)。仅仅因为您在M2代码库中找到了某些东西,并不意味着“这是实现它的最佳方法”。有时只是“我们还没有解决它”。

更少残酷:根据其他答复,有时您必须使用它,因为别无选择。其他时候可能是出于向后兼容的原因。有时框架代码直接使用是有意义的,因为它是框架代码。但是,如果我不得不在不看代码的情况下进行猜测,那么确实应该修复许多问题,但是这样做的优先级还不够高。

只要记住良好的育儿建议即可:“孩子们,请照我说的去做,不要照我做!”


9
极好的报价:孩子们,按我说的做,而不是我做的!
sivakumar

那不是
孩子的

是否有Magento 2推荐的方法来在没有对象管理器的情况下出现软件依赖问题?我有一个对另一个模块有软依赖性的模块(如果模块存在,它将加载另一个类)。我不能在该类中进行DI学习,因为DI将会失败。我什至不能为那个班级去工厂,因为工厂将不能去工厂。
内森·美林

50

您永远不要使用\Magento\Framework\App\ObjectManager::getInstance()
它达不到依赖注入的目的。我们回到了Mage::getModel()
对象管理器应仅在工厂中使用,然后注入构造函数中。

使用此方法的好处是更少的代码编写。但这并不能解决问题。
之所以仍在核心中使用该事实,是因为尚未对其进行重构。我希望会的。


5
所以我们都同意Magento代码做错了对吗?
拉斐尔(Raphael)在

11
对。他们错了 :)。
马里斯(Marius)

我不认为他们使用的是错误的。他们在必要时使用它:需要动态解析(尤其是插件)时,以及使BC使用立即弃用的方法时。
nevvermind

2
@nevvermind使用工厂。您用于di.xml创建键=>类名映射,并将该映射注入到工厂的构造函数中,并使用工厂通过objectmanager
Marius

2
@nevvermind但是,Magento员工的意见高于您的意见。您在KAndy上面有一个答案,该答案用粗体表示“您不应该直接使用对象管理器”:magento.stackexchange.com/a/117103/146我想这种方式可以消除此问题。
马里斯(Marius)

22

Magento为什么要做他们建议我们不要做的事情?这是否意味着在某些情况下我们应该直接使用ObjectManager?如果是这样,那是什么情况?

我不知道完整的故事,这是我的猜测:

在M2的发展Magento的球队在某个阶段运行的自动化脚本替代的出现Mage:getModel()Mage::getSingleton()$layout->createBlock()等使用的ObjectManager。

以后的重构应该对此进行修复,以改为使用适当的依赖项注入,但是没有足够的时间/资源来转换所有事件。

Magento团队最近也似乎将其用作逃生机制。无需破坏现有的实现(通过更改构造函数),它们只需通过ObjectManager隐藏新的依赖项即可。我不能说我同意这种方法-编写更糟糕的代码以避免BC中断。

直接使用ObjectManager的直接后果是什么?

我认为您的问题已经包含足够的理由。通常,它会创建一个隐藏的依赖关系,换句话说,该依赖关系位于实现细节中,而仅从构造函数中不可见。


具有讽刺意味的是,在向公众发布之前,在此之前做得很好,BC根本就不会成为问题
Robbie Averill,

12

不应该直接使用对象管理器!

例如:

\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以下的版本,因此请注意。根据新的Magento 2标准,我们现在甚至不能使用objectManager实例。我们必须使用对象类或存储库的工厂来获取任何数据。


以这种方式使用它是一种好习惯吗?
enrico69 17/09/29

是的,因为magento不允许使用直接的objectManager,所以您必须使用这种方式!
罗纳克·乔汉

您也不应该在事件(我想是指观察员)和插件中使用它。您应该注入所需的对象,而不是ObjectManager。只有在工厂中,您才可以使用ObjectManager,然后确实应该注入它而不是调用::getInstance()
7ochem

正确,编辑答案@ 7ochem
Ronak Chauhan

否决任何答案是不合适的方法,如果您有更好的知识,则可以添加自己的答案,也可以编辑其他任何人的答案以获得更好的主意并对他人有所帮助。@ 7ochem
Ronak Chauhan

10

不鼓励开发人员直接使用对象管理器的主要原因是,直接使用对象管理器会导致扩展无法在编译发行模式下安装。

因此,它对使用发布模式的客户(包括Magento Cloud上的所有客户)不利。

似乎相当大比例的开发人员(大约75%)不测试其扩展包以查看是否可以在发布模式下安装它们,因此不要遇到ObjectManager使用不正确引起的问题。

截至2017年,Magento 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.