So vermeiden Sie Verstöße gegen das Demeter-Gesetz („Neue Objekte sollten keinen Feldverweis auf ein injizierbares Objekt enthalten“)


7

In den Regeln für die Verwendung der Abhängigkeitsinjektion geben die Magento 2-Devdocs Folgendes an:

Newable-Objekte sollten weder einen Feldverweis auf ein injizierbares Objekt enthalten noch einen in ihrem Konstruktor anfordern. Dies ist ein Verstoß gegen das Gesetz von Demeter .

Ich verstehe, dass dies ein gutes Ziel ist, aber wie ist dies mit Magento 2-Modellen tatsächlich möglich?

Wenn wir einen Blick auf das Kundenmodul haben, die als leuchtendes Beispiel für die neue Architektur vorgestellt wurde, werden die Kunden Modell sieht Konstruktor Signatur wie folgt aus :

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 = []
)

Und Magento\Framework\Model\Contextallein, was von allen Modellen verwendet wird, benötigt fünf injizierbare Argumente.

Anscheinend sind Modelle, obwohl neuwertig , überhaupt kein gutes Beispiel für diese Regel.

Ich möchte Best Practices mit meinen eigenen Klassen (nicht einmal Modellen) befolgen, benötige jedoch Zugriff auf Dinge wie Repositorys verwandter Entitäten oder auf den Event-Manager. Was wäre der bevorzugte Weg, um damit umzugehen, ohne die Regel von oben zu brechen?

Ich neige derzeit dazu, es zu ignorieren oder es bestenfalls als freundliche Richtlinie zu betrachten, nicht in der Regel.

Antworten:


2

In diesem Beispiel muss ein großer Teil der Modelllogik in externe Serviceklassen extrahiert werden, die Code für das Modell ausführen.

Auf diese Weise würde die Repository-Klasse beim Laden des Modells über das Repository die Abhängigkeiten von den neuen Serviceklassen haben, die die Abhängigkeiten von den Klassen haben würden, die sich derzeit im Konstruktor des Modells befinden.

Ich denke, als Erweiterungsentwickler könnten wir versuchen, uns an die Regeln zu halten, aber Magento muss noch viel umgestalten.

=== edit ===

Wir haben etwas Neues gefunden. Im Kundenmodul ist das Modell in ein Datenmodell und eine Serviceklasse (das ursprüngliche Modell) unterteilt.

Für das DataInterface wird jetzt das DataModel injiziert: https://github.com/magento/magento2/blob/develop/app/code/Magento/Customer/etc/di.xml#L17

Das Datenmodell enthält nur die Daten und keine Geschäftslogik: https://github.com/magento/magento2/blob/develop/app/code/Magento/Customer/Model/Data/Customer.php

Das ursprüngliche Modell kann nun als Serviceklasse angesehen werden, die die Geschäftslogik enthält, sodass es völlig in Ordnung ist, auf andere injizierbare Elemente zu verweisen.


Guter Fund. Das ursprüngliche Modell ist jedoch immer noch nicht injizierbar . Vielleicht ist dies nur ein Schritt des Refactorings? Und wir sollten "keine Abhängigkeiten von Diensten" nicht mit "keine Geschäftslogik" verwechseln. Modelle auf einfache Datenobjekte herunterzudrehen, ist das andere Extrem und meiner Meinung nach nichts, wonach man streben sollte.
Fabian Schmengler

1
ok, aber es ist schwierig, viel Geschäftslogik einzubeziehen, ohne injizierbare Elemente zu injizieren (das klingt verwirrend). Ich denke, es ist ein guter Schritt, sie zu teilen. Das ursprüngliche Modell wird injizierbar, sobald es seine Identität verliert, nicht wahr? Ich denke, die Speicher- und Lademethoden zu verwerfen und das Datenmodell zu extrahieren, war der erste Schritt dazu. Der nächste Schritt wäre vielleicht, das komplette "alte" Modell loszuwerden und die Funktionen an geeignetere, kleinere Klassen zu delegieren
David Verholen

1

Wir versuchen, die Benutzer davon abzuhalten, direkt auf den Code in einem Modul zuzugreifen, und stattdessen eine kontrollierte, genau definierte API zu verwenden. Ziel ist es, reibungslosere Updates zwischen den Releases zu ermöglichen. Je mehr Benutzer eine definierte API verwenden, desto reibungsloser werden die Upgrades.

Ein wichtiges Konzept in den Dateien di.xml ist das Element "Einstellungen". Der Code kann auf Schnittstellendefinitionen verweisen (und ist daher nicht an eine bestimmte Implementierung gebunden). Wenn Sie jedoch nach einer Instanz fragen, weiß er, welche Klasse verwendet werden soll.

Wenn Sie also eine CustomerInterface-Instanz möchten (das ist eine Klasse, die die Schnittstelle implementiert), fragen Sie nach einer Instanz der Schnittstelle (Sie suchen nicht nach der Implementierungsklasse). Wir werden nach einem Element für Sie suchen und dieses verwenden, um die Implementierungsklasse zu identifizieren. Auf diese Weise könnte ein anderes Modul die Dateipräferenz di.xml ersetzen und in eine andere Implementierungsklasse wechseln (wenn es einen guten Grund dafür gibt).

Wenn Sie Objekte auf diese Weise über den Objektmanager erstellen, müssen Sie die Argumente für die Konstruktorliste nicht angeben. Der Objektmanager verwendet erneut die Datei di.xml, um alle erforderlichen Datenstrukturen zu finden, und stellt sie automatisch dem Konstruktor zur Verfügung.

Der Unterschied zwischen "Serviceklassen" und "Datenmodellen" in einer separaten Antwort besteht darin, dass "Serviceklassen" (von denen ich annehme, dass sie die Schnittstellen im Api-Verzeichnis bedeuten) Methoden zum Aufrufen bereitstellen. Die "Datenmodelle" (von denen ich annehme, dass sie die Schnittstellen im Api / Data-Verzeichnis bedeuten) sind Datenstrukturen, die an / von "Serviceklassen" übergeben werden. Das Ziel ist, dass jemand außerhalb des Moduls nicht wissen muss, welche Klasse die Schnittstellen implementiert. Es könnte ein Modell sein, es könnte ein Wertobjekt sein, es sollte keine Rolle spielen, solange Sie auf die Schnittstellen programmieren und niemals Klassennamen direkt erwähnen (außer in der Datei di.xml).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.