Hintergrund
Ich habe zurzeit eine Situation, in der ich ein Objekt habe , das von einem Gerät gesendet und empfangen wird. Diese Nachricht hat mehrere Konstrukte wie folgt:
public void ReverseData()
public void ScheduleTransmission()
Die ScheduleTransmission
Methode muss die ReverseData
Methode immer dann aufrufen , wenn sie aufgerufen wird. Es gibt jedoch Zeiten, in denen ich ReverseData
extern aufrufen muss (und das Hinzufügen sollte vollständig außerhalb des Namespaces erfolgen ), von wo aus das Objekt in der Anwendung instanziiert wird.
Was das "Empfangen" betrifft, meine ich, dass ReverseData
es in einem object_received
Event-Handler extern aufgerufen wird, um die Daten rückgängig zu machen.
Frage
Ist es allgemein akzeptabel, dass ein Objekt seine eigenen öffentlichen Methoden aufruft?
c#
design-patterns
object-oriented
methods
Schnüffeln
quelle
quelle
Antworten:
Ich würde sagen, es ist nicht nur akzeptabel, sondern wird auch empfohlen, insbesondere wenn Sie Erweiterungen zulassen möchten. Um Erweiterungen der Klasse in C # zu unterstützen, müssen Sie die Methode gemäß den nachstehenden Kommentaren als virtuell kennzeichnen. Möglicherweise möchten Sie dies jedoch dokumentieren, damit sich niemand wundert, wenn das Überschreiben von ReverseData () die Funktionsweise von ScheduleTransmission () ändert.
Es kommt wirklich auf das Design der Klasse an. ReverseData () klingt nach einem grundlegenden Verhalten Ihrer Klasse. Wenn Sie dieses Verhalten an anderen Stellen verwenden müssen, möchten Sie wahrscheinlich keine anderen Versionen davon haben. Sie müssen nur darauf achten, dass keine für ScheduleTransmission () spezifischen Details in ReverseData () gelangen. Das wird Probleme verursachen. Aber da Sie dies bereits außerhalb der Klasse verwenden, haben Sie sich das wahrscheinlich schon überlegt.
quelle
ReverseData()
sei denn, es ist abstrakt, egal was Sie in der untergeordneten Klasse tun, es ist immer die ursprüngliche Methode, die aufgerufen wird.virtual
... Und wenn Sie die Methode überschreiben, muss es per Definitionvirtual
oder seinabstract
.virtual
funktioniert ja auch. In allen Fällen lautet die Signatur laut OP, daher istpublic void ReverseData()
der Teil der Antwort über übergeordnetes Material leicht irreführend.abstract
? Ich habe Antworten aufabstract
vs nachgeschlagenvirtual
und das Erwähnte nicht gesehen: eher abstrakt bedeutet nur, dass in dieser Basis keine Version angegeben ist.Absolut.
Die Sichtbarkeit einer Methode hat nur den Zweck, den Zugriff auf eine Methode außerhalb der Klasse oder innerhalb einer untergeordneten Klasse zuzulassen oder zu verweigern. Öffentliche, geschützte und private Methoden können immer innerhalb der Klasse selbst aufgerufen werden.
Es ist nichts Falsches daran, öffentliche Methoden aufzurufen. Die Abbildung in Ihrer Frage ist das perfekte Beispiel für eine Situation, in der zwei öffentliche Methoden genau das sind, was Sie tun sollten.
Sie können jedoch sorgfältig auf die folgenden Muster achten:
Eine Methode
Hello()
ruft folgendes aufWorld(string, int, int)
:Vermeiden Sie dieses Muster. Verwenden Sie stattdessen optionale Argumente . Optionale Argumente erleichtern die Auffindbarkeit: Der Aufrufer, der eingibt,
Hello(
wird nicht unbedingt wissen, dass es eine Methode gibt, mit der die Methode mit Standardwerten aufgerufen werden kann. Optionale Argumente sind ebenfalls selbstdokumentierend.World()
zeigt dem Anrufer nicht die tatsächlichen Standardwerte an.Eine Methode
Hello(ComplexEntity)
ruft folgendes aufWorld(string, int, int)
:Verwenden Sie stattdessen Überladungen . Gleicher Grund: bessere Auffindbarkeit. Der Anrufer kann alle Überladungen durch IntelliSense sofort sehen und die richtige auswählen.
Eine Methode ruft einfach andere öffentliche Methoden auf, ohne einen wesentlichen Wert hinzuzufügen.
Denke nochmal nach. Benötigen Sie diese Methode wirklich? Oder sollten Sie es entfernen und den Aufrufer die verschiedenen Methoden aufrufen lassen? Ein Tipp: Wenn der Name der Methode nicht richtig oder schwer zu finden ist, sollten Sie ihn wahrscheinlich entfernen.
Eine Methode validiert die Eingabe und ruft dann die andere Methode auf:
Sollte stattdessen
World
seine Parameter selbst validieren oder privat sein.quelle
Wenn etwas öffentlich ist, kann es jederzeit von jedem System aufgerufen werden. Es gibt keinen Grund, warum Sie nicht auch eines dieser Systeme sein können!
In hochoptimierten Bibliotheken möchten Sie das in angegebene Idiom kopieren
java.util.ArrayList#ensureCapacity(int)
.Kapazität sicherstellen
ensureCapacity
ist öffentlichensureCapacity
verfügt über alle erforderlichen Grenzwertprüfungen und Standardwerte usw.ensureCapacity
AnrufeensureExplicitCapacity
verifyCapacityInternal
ensureCapacityInternal
ist privatensureCapacityInternal
Die Fehlerprüfung ist minimal, da alle Eingaben aus der Klasse stammenensureCapacityInternal
Ruft AUCH anensureExplicitCapacity
verifyExplicitCapacity
ensureExplicitCapacity
ist AUCH privatensureExplicitCapacity
hat keine FehlerprüfungensureExplicitCapacity
erledigt die eigentliche ArbeitensureExplicitCapacity
wird nur vonensureCapacity
und angerufenensureCapacityInternal
Auf diese Weise erhält Code, dem Sie vertrauen, privilegierten (und schnelleren!) Zugriff, da Sie wissen, dass seine Eingaben gut sind. Code, dem Sie nicht vertrauen, durchläuft eine gewisse Strenge, um seine Integrität und Bombe zu überprüfen oder Standardeinstellungen bereitzustellen oder auf andere Weise mit schlechten Eingaben umzugehen. Beide leiten zu dem Ort, an dem tatsächlich gearbeitet wird.
Dies wird jedoch in ArrayList verwendet, einer der am häufigsten verwendeten Klassen im JDK. Möglicherweise erfordert Ihr Fall nicht diese Komplexität und Genauigkeit. Kurz gesagt, wenn alle
ensureCapacityInternal
Anrufe durchensureCapacity
Anrufe ersetzt würden, wäre die Leistung immer noch sehr, sehr gut. Dies ist eine Mikrooptimierung, die wahrscheinlich erst nach eingehender Prüfung vorgenommen wird.quelle
Es wurden bereits mehrere gute Antworten gegeben, und ich stimme ihnen auch zu, dass ein Objekt seine öffentlichen Methoden möglicherweise von seinen anderen Methoden aufruft. Es gibt jedoch eine kleine Einschränkung beim Design, auf die Sie achten müssen.
Öffentliche Methoden haben normalerweise den Auftrag, "das Objekt in einen konsistenten Zustand zu versetzen, etwas Sinnvolles zu tun, das Objekt in einem (möglicherweise anderen) konsistenten Zustand zu belassen". Hier kann "konsistent" zum Beispiel bedeuten, dass das
Length
von aList<T>
nicht größer als sein istCapacity
und dass das Verweisen der Elemente auf Indizes von0
bisLength-1
nicht werfen wird.In den Methoden des Objekts befindet sich das Objekt möglicherweise in einem inkonsistenten Zustand. Wenn Sie also eine Ihrer öffentlichen Methoden aufrufen, kann dies sehr falsch sein, da dies nicht im Hinblick auf eine solche Möglichkeit geschrieben wurde. Wenn Sie also vorhaben, Ihre öffentlichen Methoden von Ihren anderen Methoden aufzurufen, stellen Sie sicher, dass ihr Vertrag lautet: "Nehmen Sie das Objekt in einen bestimmten Zustand, tun Sie etwas Sinnvolles, belassen Sie das Objekt in einem (möglicherweise anderen) (möglicherweise inkonsistenten) Zustand - aber nur, wenn der Anfangszustand erreicht ist state war inkonsistent) state ".
quelle
Ein weiteres Beispiel für den Aufruf einer öffentlichen Methode innerhalb einer anderen öffentlichen Methode ist ein CanExecute / Execute-Ansatz. Ich benutze es, wenn ich sowohl eine Validierung als auch eine invariante Konservierung benötige .
Aber im Allgemeinen bin ich immer vorsichtig. Wenn eine Methode
a()
innerhalb von method aufgerufen wirdb()
, bedeutet dies, dass methoda()
ein Implementierungsdetail von istb()
. Sehr oft zeigt es an, dass sie zu verschiedenen Abstraktionsebenen gehören. Und die Tatsache, dass beide öffentlich sind, lässt mich fragen, ob es sich um einen Verstoß gegen das Prinzip der einmaligen Verantwortung handelt oder nicht.quelle
Tut mir leid, aber ich muss den meisten anderen "Ja, Sie können" -Antworten nicht zustimmen und Folgendes sagen:
Ich würde eine Klasse davon abhalten, eine öffentliche Methode von einer anderen aufzurufen
Es gibt ein paar mögliche Probleme mit dieser Praxis.
1: Endlosschleife in inhertited Klasse
Ihre Basisklasse ruft also method1 von method2 auf, aber dann erben Sie oder jemand anderes davon und verbergen method1 mit einer neuen Methode, die method2 aufruft.
2: Ereignisse, Protokollierung usw.
Ich habe zB eine Methode Add1, die ein Ereignis '1 added!' auslöst. Ich möchte wahrscheinlich nicht, dass die Add10-Methode dieses Ereignis auslöst, zehnmal in ein Protokoll schreibt oder was auch immer.
3: Threading und andere Deadlocks
Beispiel: InsertComplexData öffnet eine Datenbankverbindung, startet eine Transaktion, sperrt eine Tabelle, ruft dann InsertSimpleData auf, öffnet eine Verbindung, startet eine Transaktion und wartet, bis die Tabelle entsperrt ist.
Ich bin mir sicher, dass es weitere Gründe gibt. Eine der anderen Antworten betraf "Sie bearbeiten Methode1 und sind überrascht, dass Methode2 sich anders verhält".
Im Allgemeinen ist es besser, wenn Sie zwei öffentliche Methoden haben, die Code gemeinsam nutzen, beide eine private Methode aufrufen zu lassen, anstatt die eine die andere aufzurufen.
Bearbeiten ----
Lassen Sie uns auf den speziellen Fall im OP eingehen.
Wir haben nicht viele Details, aber wir wissen, dass ReverseData von einer Ereignisbehandlungsroutine wie auch von der ScheduleTransmission-Methode aufgerufen wird.
Ich gehe davon aus, dass umgekehrte Daten auch den internen Zustand des Objekts ändern
In diesem Fall würde ich denken, dass Thread-Sicherheit wichtig wäre, und daher gilt mein dritter Einwand gegen die Praxis.
Um den ReverseData-Thread sicherer zu machen, können Sie eine Sperre hinzufügen. Wenn ScheduleTransmission jedoch auch threadsicher sein muss, möchten Sie dieselbe Sperre freigeben.
Am einfachsten ist es, den ReverseData-Code in eine private Methode zu verschieben und von beiden öffentlichen Methoden aufrufen zu lassen. Anschließend können Sie die lock-Anweisung in die öffentlichen Methoden einfügen und ein Sperrobjekt freigeben.
Offensichtlich kann man argumentieren, "das wird niemals passieren!" oder "Ich könnte die Sperre auf eine andere Weise programmieren", aber der Punkt bei einer guten Codierungspraxis besteht darin, den Code zunächst gut zu strukturieren.
In akademischen Begriffen würde ich sagen, dass dies das L in soliden verletzt. Öffentliche Methoden sind mehr als nur öffentlich zugänglich. Sie können auch von ihren Erben geändert werden. Ihr Code sollte für Änderungen geschlossen sein, was bedeutet, dass Sie überlegen müssen, was Sie sowohl mit öffentlichen als auch mit geschützten Methoden tun.
Hier ist noch einer: Sie verletzen möglicherweise auch DDD. Wenn es sich bei Ihrem Objekt um ein Domänenobjekt handelt, sollten die öffentlichen Methoden Domänenbegriffe sein, die für das Unternehmen von Bedeutung sind. In diesem Fall ist es sehr unwahrscheinlich, dass "ein Dutzend Eier kaufen" dasselbe ist wie "1 Ei 12-mal kaufen", auch wenn es so beginnt.
quelle