デメテルの法則違反を回避する方法(「新規オブジェクトは、注入可能なオブジェクトへのフィールド参照を保持してはなりません」)


7

依存性注入を使用するためルールで、Magento 2 devdocsは次のように述べています。

Newableオブジェクトは、注入可能なオブジェクトへのフィールド参照を保持したり、コンストラクターでフィールド参照を要求したりしてはなりません。これはデメテルの法則違反です。

これは良い目標だと私は理解していますが、Magento 2モデルでこれを実際にどのように実現できますか?

新しいアーキテクチャの優れた例として示されているCustomerモジュールを見ると、顧客モデルコンストラクターのシグネチャは次のようになります。

public function __construct(
    \Magento\Framework\Model\Context $context,
    \Magento\Framework\Registry $registry,
    \Magento\Store\Model\StoreManagerInterface $storeManager,
    \Magento\Eav\Model\Config $config,
    \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
    \Magento\Customer\Model\ResourceModel\Customer $resource,
    \Magento\Customer\Model\Config\Share $configShare,
    \Magento\Customer\Model\AddressFactory $addressFactory,
    \Magento\Customer\Model\ResourceModel\Address\CollectionFactory $addressesFactory,
    \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder,
    GroupRepositoryInterface $groupRepository,
    \Magento\Framework\Encryption\EncryptorInterface $encryptor,
    \Magento\Framework\Stdlib\DateTime $dateTime,
    CustomerInterfaceFactory $customerDataFactory,
    DataObjectProcessor $dataObjectProcessor,
    \Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
    \Magento\Customer\Api\CustomerMetadataInterface $metadataService,
    \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry,
    \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
    array $data = []
)

そしてMagento\Framework\Model\Context、すべてのモデルで使用されている単独で、5つの注入可能な引数を取ります。

どうやら、モデルはnewableですが、このルールの良い例ではありません。

自分のクラス(モデルではない)でベストプラクティスに従いたいのですが、関連するエンティティのリポジトリやイベントマネージャーなどにアクセスする必要があります。上からルールを破ることなくこれを処理するための好ましい方法は何でしょうか?

私は現在それを無視する傾向があります、またはせいぜい、それを原則としてではなく、友好的なガイドラインとして見ています。

回答:


2

この例では、モデルロジックの大部分を、モデルでコードを実行する外部サービスクラスに抽出する必要があると思います。

このようにして、たとえばリポジトリを介してモデルをロードする場合、リポジトリクラスは、現在モデルのコンストラクターにあるクラスに依存する新しいサービスクラスに依存します。

私は拡張機能の開発者としてルールに固執しようとすることができると思いますが、まだmagentoによって行われる多くのリファクタリングがあります。

===編集===

新しいものを見つけました。顧客モジュールでは、モデルはデータモデルとサービスクラス(元のモデル)に分かれています。

DataInterfaceの場合、DataModelが挿入されます。https//github.com/magento/magento2/blob/develop/app/code/Magento/Customer/etc/di.xml#L17

データモデルにはデータのみが含まれ、ビジネスロジックは含まれません。https//github.com/magento/magento2/blob/develop/app/code/Magento/Customer/Model/Data/Customer.php

そのため、元のモデルはビジネスロジックを含むサービスクラスと見なすことができ、他のインジェクタブルを参照してもまったく問題ありません。


良い発見。しかし、元のモデルはまだ注射可能ではありません。たぶん、これはリファクタリングの1つのステップにすぎませんか?また、「サービスへの依存なし」と「ビジネスロジックなし」を混同しないでください。モデルを単純なデータオブジェクトに調整することは、もう1つの極端なことであり、私見では努力する必要はありません。
Fabian Schmengler 2016年

1
わかりましたが、注射剤を注入せずに多くのビジネスロジックを含めることは困難です(これは混乱を招くようです)。したがって、それらを分割することは良いステップだと思います。元のモデルは、アイデンティティを失うとすぐに注射可能になりますね。saveメソッドとloadメソッドを廃止し、データモデルを抽出することが、これを行う最初のステップだと思います。次のステップは、おそらく完全な「古い」モデルを取り除くことであり、機能をより適切な小さなクラスに委任することになるでしょう
David Verholen

1

私たちは、人々がモジュール内のコードに直接アクセスするのを避け、代わりに、より制御され、明確に定義されたAPIを通過しようとしています。目標はリリース間のスムーズな更新を可能にすることです-定義されたAPIをより多くの人々が使用するほど、スムーズなアップグレードになります。

di.xmlファイルの重要な概念は、「preference」要素です。これにより、コードはインターフェース定義を参照できます(したがって、特定の実装に関連付けられません)が、インスタンスを要求すると、使用するクラスがわかります。

したがって、CustomerInterfaceインスタンス(インターフェースを実装するクラス)が必要な場合は、インターフェースのインスタンスを要求します(実装クラスを探す必要はありません)。要素を探し、それを使用して実装クラスを識別します。そうすることで、別のモジュールがdi.xmlファイル設定を置き換え、別の実装クラスに入れ替えることができます(正当な理由がある場合)。

また、この方法でオブジェクトマネージャーを介してオブジェクトを作成する場合、コンストラクターリストに引数を指定する必要はありません。オブジェクトマネージャーは再びdi.xmlファイルを使用して必要なデータ構造をすべて検索し、それらを自動的にコンストラクターに提供します。

別の応答での「サービスクラス」と「データモデル」の違いは、「サービスクラス」(Apiディレクトリ内のインターフェースを意味する)が、呼び出すメソッドを提供することです。「データモデル」(Api / Dataディレクトリ内のインターフェースを意味します)は、「サービスクラス」との間でやり取りされるデータ構造です。目標は、モジュールの外部の誰かがインターフェースを実装しているクラスを知る必要がないようにすることです。これはモデルでも、値オブジェクトでもかまいません。インターフェイスにプログラミングし、クラス名を直接言及しない限り、問題はありません(di.xmlファイル以外)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.