Demetergesetz und Datenmodelle?

2199
Bolo

Inspiriert von dieser Frage, aber hoffentlich kein Duplikat.

Ich verstehe, dass das Demeter-Gesetz sehr nützlich ist, wenn Dienste mit anderen Diensten interagieren. Beispielsweise ist es viel einfacher, die anderen Dienste in Einzeltests zu verspotten. Was ist jedoch mit Diensten, die mit Datenmodellen interagieren?

Nehmen wir an, ich habe eine Hierarchie von unveränderlichen Klassen:

public final class Document {
    private final List<Page> pages;
    // Constructor, getter, ...
}

public final class Page {
    private final List<Paragraph> paragraphs;
    // Constructor, getter, ...
}

public final class Paragraph {
    private final List<Line> lines;
    // Constructor, getter, ...
}

Nehmen wir an, ich möchte etwas mit bestimmten Zeilen in einem Dokument machen doc:

public void doSomething(Document doc) {
    for (Page page : doc.getPages()) {
        for (Paragraph para : page.getParagraphs()) {
            for (Line line : para.getLines()) {
                if (someCondition(line)) {
                    someAction(line);
                }
            }
        }
    }

Soweit ich weiß, gehorcht der obige Kodex dem Gesetz von Demeter nicht. Ich möchte jedoch das Gesetz nicht um jeden Preis erzwingen und das Modell (hier die DocumentKlasse) mit Dutzenden von Methoden durcheinander bringen:

public List<Line> filterWrtSomeCondition();
public List<Line> filterWrtSomeOtherCondition();

Oder gibt es vielleicht einen dritten Weg? Ich vermute, dass das Demeter-Gesetz in erster Linie für Dienste gilt, die mit anderen Diensten interagieren. Ist es eine gültige Interpretation?

Antworten
22

4 Antworten auf die Frage

16
LRE

Lesen Sie für ein wenig Perspektive auf das LoD " Das Gesetz von Demeter ist keine Punktzählung ". Insbesondere der Teil unter der Überschrift "Ich habe das Gesetz und das Gesetz gewonnen".

Ich denke in diesem Fall, dass Seiten, Absätze und Zeilen sehr logische Unterkomponenten von Dokumenten sind. Diese haben jeweils ein anderes, aber verwandtes Verhalten. Sie dienen auch unterschiedlichen Zwecken.

Wenn Sie das Dokument zur Interaktion mit Unterkomponenten zwingen, führt dies, wie Sie sagen, zu Unordnung (und mehr Möglichkeiten, problematische Probleme einzuleiten).

Das große an der LoD ist, dass die Kopplung reduziert werden soll. Das Hinzufügen von Methoden zu Document reduziert jedoch nicht wirklich die Kopplung, da Sie immer noch Code schreiben, der "do x to line y" sagt - Sie fordern lediglich Document auf, dies für Sie zu tun.

Versteht mich nicht falsch, das LoD ist nützlich. Aber wie alle Prinzipien muss es richtig verwendet werden, um nützlich zu bleiben.

(Merkwürdigerweise eine ähnliche Frage mit einer ähnlichen Antwort, die bei SO gestellt wurde.)


Fazit

Kurz gesagt, ich sehe keinen Nutzen für Sie, wenn Sie mit Document sprechen, statt mit Pages, Paras & Lines. Ehrlich gesagt glaube ich nicht, dass es einen ROI für Sie gibt.

Interessante Referenz, insbesondere: "Wenn es sich bei LoD um Verkapselung handelt (warum sich Informationen verstecken), warum sollten Sie dann die Struktur eines Objekts ausblenden, bei der die Struktur genau das ist, woran die Leute interessiert sind und sich wahrscheinlich nicht ändern werden?". Ich neige dazu zu denken, dass OO-Entwurfsprinzipien, die für Objekte mit Verantwortlichkeiten und Verhalten gedacht sind, bei der Arbeit mit Datenstrukturen, die wirklich eine andere Art von Objekten sind, nicht berücksichtigt werden sollten. Eric Bréchemier vor 9 Jahren 3
12
Bert F

Ich werde den Kontrapunkt bestreiten ...

Das LoD würde sagen, dass dieser Clientcode an ein bestimmtes Modell des Dokuments gebunden ist. Am Ende interessiert sich der Code wirklich für die Zeilen eines Dokuments. Wie sinnvoll ist es, dass wir diesen Code an das getPage / getParagraph / getLineModell binden ?

Es ist absolut vernünftig zu glauben, dass Seiten immer aus Absätzen und Absätzen bestehen, die aus Zeilen bestehen, aber ich denke nicht, dass dies die einzige Überlegung ist. Die andere Frage ist, welche anderen Modelle Document unterstützen sollte. Anders ausgedrückt:

"Ist es sinnvoll, ein Dokument als eine Reihe von Zeilen zu betrachten, unabhängig davon, auf welcher Seite oder in welchem ​​Absatz sie sich befinden?"

Ich denke es ist, also würde ich Documenteine getLines()Methode angeben und nicht unseren Code zwingen, das Page / Paragraph / Line-Modell durchzugehen.

Diese getLines()Methode ist auch eine gute Sache, denn: Wenn es für unseren DocumentKunden sinnvoll ist, Documentdie Dinge auf diese Weise zu sehen, ist es vernünftig, dass die Dinge auch so gesehen werden . Das heißt, ist es nicht möglich, dass die Document möglicherweise eine effiziente Struktur für die Bewegung von Zeile zu Zeile (wenn nicht jetzt, dann vielleicht in der Zukunft), die nicht den Seiten und Absätze beinhalten kann? Wenn dies der getPage / getParagraph / getLineFall ist, schadet das Erzwingen von Clientcode durch das Durchlaufen der Möglichkeit, diese effiziente Struktur zu verwenden.

Ich denke auch, dass es unvernünftig ist, "das Dokument zu zwingen, Interaktionen mit Unterkomponenten zu handhaben" in jedem erdenklichen Fall, so dass es da draußen ein glückliches Medium geben muss. Betrachten Sie als Gegenbeispiel:

"Ist es sinnvoll, eine Bibliothek als eine Reihe von Zeilen zu betrachten, ohne auf das Dokument zu achten, in dem sie sich befinden?"

Ich glaube nicht, dass ich getLines()meiner LibraryKlasse eine Methode geben werde .

3
mtnygard

Ich stimme auch zu, dass dieser Code gegen LoD verstößt. Der Aufrufer bindet die Implementierung an ein Modell verschachtelter Objekte.

Das Hinzufügen mehrerer Filtermethoden scheint auch nicht die richtige Idee zu sein, aber dies ist eine verdeckte Gelegenheit. Sie möchten wirklich eine allgemeine Filtermethode, die ein "Verb" benötigt, um auf die übereinstimmenden Elemente angewendet zu werden. Oder alternativ eine Filtermethode, die ein Vergleichselement verwendet und nur die Sammlung übereinstimmender Elemente zurückgibt.

Dies ist eines der nützlichen Dinge über LoD. Es hilft Ihnen, Orte zu entdecken, an denen Sie auf einem niedrigen Abstraktionsniveau arbeiten, und das Niveau zu erhöhen. In diesem Fall werden Sie, sobald Sie allgemeine Prädikate oder Verben eingeführt haben, sehr wahrscheinlich andere Orte finden, an denen dieselbe Redewendung verwendet wird. Was als lokale Bereinigung beginnt, kann eine sehr allgemeine Ausdrucksweise aufdecken.

2
Mark Loeser

Ich würde sagen, dass es das Modell nicht persönlich stört. Sie schützen sich tatsächlich, indem Sie die interne Implementierung Ihrer Dokumentenklasse nicht offenlegen. Die gesamte Idee ist, Ihr gesamtes Verhalten in Ihre Klasse zu integrieren, anstatt die Klasse von externem Code so modifizieren zu lassen, dass sie ihre eigenen internen Invarianten nicht überschreitet.

Vielleicht wäre es auch sinnvoll, Unterklassen Ihrer Document-Klasse zu erstellen, die spezialisiert sind und nur über die Methoden verfügen, die sich auf die Verwendung Ihres Dokuments beziehen. Sie brauchen keinen One-Stop-Shop, in dem Sie alles tun können, da dies wahrscheinlich nicht in allen Fällen Ihr primärer Anwendungsfall ist.

Danke für deine Antwort! Obwohl ich das nicht betont habe, habe ich erwähnt, dass das Modell unveränderlich ist. Es gibt also keine Möglichkeit, es zu "brechen", sobald es erstellt wurde. Ändert das etwas? Bolo vor 9 Jahren 0
Ich bin jedoch skeptisch, Unterklassen hinzuzufügen. Zum einen kann ich die `Document'-Klasse eindeutig benennen und dokumentieren, aber wie benenne ich eine` Document'-Unterklasse mit Methoden zur Bewertung der Lesbarkeit von Text oder mit Methoden zum Auffinden von Vorkommen einer gegebenen Phrase? Wie in: "Wenn Sie es nicht benennen können, sollte es keine Klasse sein." Bolo vor 9 Jahren 2
Ich habe mich nicht an den unveränderlichen Teil gewöhnt. Es macht es zumindest so, dass Sie es nicht brechen können, aber die Veröffentlichung der internen Implementierung ist immer noch gültig. Ohne Ihren Anwendungsfall zu kennen, vermute ich nur und sage, dass Unterklassen ein gültiger Weg sein könnten, um Ihre Bedenken hinsichtlich des Hinzufügens vieler Methoden zu berücksichtigen (sie können zerbrochen sein). Mark Loeser vor 9 Jahren 0