2009-03-05 12 views
5

Ich bin das jetzt ein paar Mal begegnet, und ich habe mich gefragt, was der OO-Weg ist, um Zirkelverweise zu lösen. Damit meine ich, Klasse A hat Klasse B als Mitglied, und B wiederum hat Klasse A als Mitglied.Wie löst man Querverweise in OOP?

Ein Beispiel hierfür wäre die Klasse Person, die den Ehepartner der Person als Mitglied hat.

Ein anderes Beispiel wäre Produktklassen, die eine Sammlung anderer Produkte als Mitglied haben. Diese Sammlung könnte zum Beispiel Produkte sein, an denen auch Personen interessiert sein könnten, die an diesem Produkt interessiert sind, und wir möchten diese Liste auf einer Pro-Produkt-Basis aufrechterhalten, nicht auf denselben geteilten Attributen (zB wollen wir nicht nur anzeigen alle anderen Produkte derselben Kategorie).

Product pc = new Product("pc"); 
Product monitor = new Product("monitor"); 
Product tv = new Product("tv"); 
pc.setSeeAlso({monitor, tv}); 
monitor.setSeeAlso({pc}); 
tv.setSeeAlso(null); 

(diese Produkte sind nur noch einen Punkt für die Herstellung, das Problem nicht etwa, ob oder nicht bestimmte Produkte miteinander in Beziehung stehen würden)

Wäre dies schlechtes Design in OOP im Allgemeinen? Sollten/sollten alle OOP-Sprachen dies zulassen, oder ist es nur eine schlechte Übung? Wenn es eine schlechte Übung ist, was wäre der beste Weg, dies zu lösen?

+0

@Jon Limjap: Danke für die Korrektur des Tippfehlers. – tehvan

Antwort

1

Es ist kein grundlegendes Problem in OO-Design. Ein Beispiel für eine Zeit, in der es zu einem Problem kommen könnte, ist die Traversierung von Graphen, zum Beispiel, um den kürzesten Pfad zwischen zwei Objekten zu finden - Sie könnten möglicherweise in eine Endlosschleife gelangen. Das müssen Sie jedoch von Fall zu Fall berücksichtigen. Wenn Sie wissen, dass es in einem solchen Fall Querverweise geben könnte, dann kodieren Sie einige Eincheckvorgänge, um Endlosschleifen zu vermeiden (z. B., einen Satz von besuchten Knoten zu warten, um einen erneuten Besuch zu vermeiden). Aber wenn es keinen Grund gibt, dass es ein Problem sein könnte (wie in den Beispielen, die Sie in Ihrer Frage gegeben haben), dann ist es überhaupt nicht schlecht, solche Querverweise zu haben. Und in vielen Fällen, wie Sie beschrieben haben, ist es eine gute Lösung für das vorliegende Problem.

2

Die Beispiele, die Sie geben, sind (für mich jedenfalls) Beispiele für vernünftige OO-Design. Das von Ihnen beschriebene Problem der Querverweise ist kein Artefakt irgendeines Designprozesses, sondern eine reale Eigenschaft der Dinge, die Sie als Objekte darstellen. Daher sehe ich kein Problem.

Was haben Sie erlebt, was Ihnen den Eindruck vermittelt hat, dass dieser Ansatz schlecht ist?

-Update 11. März:

In Systemen, die Garbage Collection fehlt, wo die Speicherverwaltung explizit verwaltet wird, ein gemeinsamer Ansatz ist es, alle Objekte benötigen einen Besitzer haben - für die Verwaltung ein anderes Objekt verantwortlich die Lebensdauer dieses Objekts.

Ein Beispiel ist die TComponent-Klasse von Delphi, die kaskadierende Unterstützung bietet - die übergeordnete Komponente wird zerstört und alle eigenen Komponenten werden ebenfalls zerstört.

Wenn Sie auf einem solchen System arbeiten, die Arten von Referenzschleife in dieser Frage beschrieben kann schlechtes Design in Betracht gezogen werden, weil es keine klare Besitzer ist, niemand für die Verwaltung von Lebensdauern verantwortlich widersprechen. Die Art, wie ich dies in einigen Systemen behandelt habe, besteht darin, die Referenzen beizubehalten (weil sie die Geschäftsanliegen korrekt erfassen) und ein explizites Objekt TransactionContext hinzuzufügen, das alles besitzt, was in die Geschäftsdomäne geladen wird die Datenbank. Dieses Kontextobjekt sorgt dafür, dass Sie wissen, welche Objekte gespeichert werden müssen, und bereinigt alles, wenn die Verarbeitung abgeschlossen ist.

+0

Es scheint mir auch real-life, ich dachte nur nicht VMs werden verwirrt Objekt zu jagen ... Laden einer Person, Laden der Partner dieser Person, wieder der Ehepartner dieser Person, ... es ist eine ewige Schleife. Ich frage mich nur, ob alle VMs damit richtig umgehen können. – tehvan

+0

VM? Du meinst Laufzeiten? Ref-count nur Systeme (COM oder Roll-eigene Smart-Pointer, zum Beispiel) wird jedes echte GC-System nicht –

+0

Referenzzyklen werden sowohl von der CLR (.NET) und JVM-Umgebungen gut behandelt. Sie wurden auch von den frühen Lisp- und Smalltalk-Umgebungen, in denen viel von dieser Technologie ursprünglich bewiesen wurde, gut behandelt. – Bevan

1

Die Hauptzeit, die dies ein Problem ist, ist, wenn es zu verwirrend wird, um damit fertig zu werden, oder aufrechtzuerhalten, wie es eine Form von Spaghetti-Code werden kann.

Allerdings, um Ihre Beispiele zu berühren;

Siehe auch perfekt gültig ist , wenn dies ein Merkmal ist Sie in Ihrem Code benötigen - es ist eine einfache Liste von Zeigern (oder Referenzen) auf andere Elemente ein Benutzer interessiert sein

Ähnlich ist es. perfekt gültig, um den Ehepartner hinzuzufügen, da dies eine einfache echte Weltbeziehung ist, die nicht zu jemand verwechseln würde, der Ihren Code aufrechterhält.

Ich habe es immer als Potenzial Code Geruch gesehen, oder vielleicht eine Warnung, einen Schritt zurück und rationalisieren, was ich tue.

Wie bei einigen Systemen finden rekursive Beziehungen in Ihrem Code (in einem Kommentar erwähnt), diese können unabhängig von dieser Art von Design kommen. Ich habe kürzlich an einem Metadatenerfassungssystem gearbeitet, das rekursive "Arten" von Beziehungen hatte - d. H. Spalten, die logisch mit anderen Spalten verwandt sind. Es muss von dem Code behandelt werden, der versucht, Ihr System zu analysieren.

-2

Eine Möglichkeit, dies zu beheben, besteht darin, über eine ID auf ein anderes Objekt zu verweisen.

z.B.

Person jack = new Person(new PersonId("Jack")); 
Person jill = new Person(new PersonId("Jill")); 
jack.setSpouse(jill.getId()); 
jill.setSpouse(jack.getId()); 

Ich sage nicht, es ist eine perfekte Lösung, aber es wird zirkuläre Referenzen zu verhindern. Sie verwenden ein Objekt anstelle einer Objektreferenz, um die Beziehung zu modellieren.

+0

Dies ist zu viel wie eine Datenbank denken. Das Festlegen eines Verweises auf ein Objekt erreicht dasselbe mit weniger Arbeit. – Boden

+0

Wenn Sie jedoch das Objektdiagramm in einer Datenbank beibehalten möchten, ermöglicht dieser Ansatz das teilweise Laden des Objektdiagramms ohne Proxying und verhindert zirkuläre Referenzen. Wie gesagt, es ist nicht die perfekte Lösung, aber ich habe gesehen, dass es erfolgreich in einer Code-Anwendung mit mehreren Millionen Zeilen verwendet wurde. – parkr

0

Ich glaube nicht, die Zirkelverweise als solche sind ein Problem.

Wenn Sie jedoch alle diese Beziehungen in Objekte einfügen, können Sie zu viel Unordnung hinzufügen, sodass Sie sie möglicherweise lieber extern darstellen möchten. Z.B. Sie können eine Hash-Tabelle verwenden, um Beziehungen zwischen Produkten zu speichern.

1

Ich glaube nicht, dass dies ein Beispiel für Querverweise ist.

Querverweise bezieht sich in der Regel auf diesen Fall:

class A 
{ 
    public void MethodA(B objectB) 
    { 
     objectB.SomeMethodInB(); 
    } 
} 

class B 
{ 
    public void MethodB(A objectA) 
    { 
     objectA.SomeMethodInA(); 
    } 
} 

In diesem Fall jedes Objekt Art miteinander „in erreicht“; A ruft B an, B ruft A an und sie werden eng miteinander verbunden. Dies wird noch schlimmer, wenn sich A und B in verschiedenen Paketen/Namespaces/Assemblies befinden; In vielen Fällen würden diese Kompilierungszeitfehler verursachen, da Assemblys linear kompiliert werden.

Die Lösung besteht darin, dass ein Objekt eine Schnittstelle mit der gewünschten Methode implementiert.

In Ihrem Fall haben Sie nur eine Ebene von „erreicht in“:

public Class Person 
{ 
    public void setSpouse(Person person) 
    { ... } 
} 

Ich glaube nicht, das ist unvernünftig, ja nicht einmal ein Fall von Querverweisen/zirkulärer Referenzen.

0

Verweise auf andere Objekte ist kein wirklich schlechtes OO-Design. So wird der Zustand in jedem Objekt verwaltet.

Eine gute Faustregel ist das Gesetz von Demeter. Sehen Sie sich dieses perfekte Papier von LoD (Paperboy und die Brieftasche) an: click here