Eine nützliche Sache zu realisieren ist, dass Ihr Modellcode tendenziell in sich abgeschlossen ist - es weiß über Datenelemente im Modell (dh das Daten-Diagramm) und die Datenkonsistenzregeln, aber nichts anderes.
So Ihr Modell für eine Seite aussehen würde wahrscheinlich wie
class Page {
URL uri;
ImageCollection images;
}
Mit anderen Worten, das Modell weiß um die Beziehung zwischen den Seiten und Bildern, aber es weiß nicht unbedingt das, was diese Dinge in der Praxis bedeuten.
Um Ihr Domänenmodell tatsächlich mit der realen Welt zu vergleichen, übergeben Sie dem Modell einen Dienst, der weiß, wie die Arbeit zu erledigen ist, aber den Status nicht kennt.
class Crawler {
void verify(URL page, ImageCollection images)
}
Jetzt passen Sie sie zusammen; Sie konstruieren den Crawler und übergeben ihn an die Seite. Die Seite findet seinen Zustand und übergibt diesen Zustand zu dem Crawler
class Page {
void verifyWith(Crawler crawler) {
crawler.verify(this.uri, this.items);
}
}
Natürlich, werden Sie wahrscheinlich auf den Crawler nicht zu eng an der Seite zu koppeln wollen; Schließlich möchten Sie vielleicht die Crawler-Bibliotheken austauschen, Sie möchten vielleicht etwas anderes mit dem Seitenstatus tun.
Sie machen also die Signatur dieser Methode allgemeiner; Es akzeptiert eine Schnittstelle und kein Objekt mit einer bestimmten Bedeutung.In dem klassischen Buch Design Patterns, wäre dies ein Beispiel für den Visitor Pattern
class Page {
interface Visitor {
void visitPage(URL uri, ImageCollection images);
}
void verifyWith(Visitor visitor) {
visitor.visitPage(this.uri, this.images);
}
}
class Crawler implements Page.Visitor {
void visitPage(URL page, ImageCollection images) {
....
}
}
Hinweis sein - das Modell (Seite) ist verantwortlich für die Integrität ihrer Daten erhalten bleibt. Das bedeutet, dass alle Daten, die an einen Besucher übergeben werden, unveränderlich sein müssen, oder andernfalls eine veränderbare Kopie des Zustands des Modells.
Auf lange Sicht möchten Sie wahrscheinlich nicht die Definition des Visitors in der Seite eingebettet wie folgt. Seite ist Teil der API des Modells, aber der Besucher ist Teil der SPI des Modells.
interface PageVisitor {
void visitPage(URL uri, ImageCollection images);
}
class Page {
void verifyWith(PageVisitor visitor) {
visitor.visitPage(this.uri, this.images);
}
}
class Crawler implements PageVisitor {
void visitPage(URL page, ImageCollection images) {
....
}
}
Eine Sache, die hier beschönigt bekommen haben, ist, dass Sie zwei verschiedene Implementierungen von „Seite“ zu haben scheinen
// Here's one?
$page = new Page($url);
// And here is something else?
$pageReturned = $crawler->get($page);
Eine der Lehren von ddd ist die der Dinge zu benennen; insbesondere, dass Sie nicht zwei Ideen kombinieren, die wirklich unterschiedliche Bedeutungen haben. In diesem Fall sollten Sie sich darüber im Klaren sein, welcher Typ vom Crawler zurückgegeben wird.
Zum Beispiel, wenn Sie in einer Domäne sind, wo die allgegenwärtige Sprache von REST entlehnt, dann könnten Sie Aussagen, die
aussehen
$representation = $crawler->get($resource);
In Ihrem Beispiel sucht die Sprache mehr HTML-spezifisch, so könnte dies angemessen sein
$htmlDocument = $crawler->get($page)
der Grund für die Belichtung dieses: das Dokument/Darstellung paßt gut mit der Vorstellung, ein Wertobjekt des Seins - es ist eine unveränderliche Tasche unveränderlichen Zeugs; Sie können die "Seite" nicht ändern, indem Sie das HTML-Dokument in irgendeiner Weise manipulieren.
Wertobjekte sind reine Query-Oberflächen - jede Methode auf ihnen, die wie eine Mutation aussieht, ist wirklich eine Abfrage, die eine neue Instanz des Typs zurückgibt.
Wertobjekte sind eine gute Passform für die Spezifikation Muster von plalx in seiner Antwort beschrieben:
HtmlSpecification {
boolean isSatisfiedBy(HtmlDocument);
}
Hier kommt das D in SOLID ins Spiel - Dependency Inversion. Eine höhere Klassenklasse sollte nur von Abstraktionen abhängen, nicht von konkreten Klassen. Wenn die verschiedenen Crawling-Implementierungen eine gemeinsame Schnittstelle implementieren und der aufrufende Code nur für diese Schnittstelle funktioniert, kann die gewünschte konkrete Implementierung nach Bedarf eingefügt werden. – dbugger
Warum sich mit einem komplexen Domänenmodell beschäftigen? Das einzige wahre Objekt in dem Problem ist der "Crawler", der den Zustand beibehalten muss, während er sein Verhalten des "Crawlens" ausführt. Der Rest Ihrer "Objekte" sind nur Datenstrukturen. Hat 'Page' Zustände, zwischen denen es übergeht? Hat es ein Verhalten, das sich während seiner Lebenszeit ändert? Wenn nein, ist es nur eine Datenstruktur, die von Ihrem Crawler konsumiert wird. –