Bei der Dokumentation in OOP sollte vermieden werden, dass angegeben wird, ob ein "Getter" Berechnungen durchführt oder nicht.


39

Das CS-Programm meiner Schule vermeidet jede Erwähnung von objektorientierter Programmierung, deshalb habe ich einige Lektüre allein gemacht, um es zu ergänzen - insbesondere die objektorientierte Softwarekonstruktion von Bertrand Meyer.

Meyer weist wiederholt darauf hin, dass Klassen möglichst viele Informationen über ihre Implementierung verbergen sollten, was sinnvoll ist. Insbesondere argumentiert er wiederholt, dass Attribute (dh statische, nicht berechnete Eigenschaften von Klassen) und Routinen (Eigenschaften von Klassen, die Funktions- / Prozeduraufrufen entsprechen) nicht voneinander zu unterscheiden sind.

Wenn zum Beispiel eine Klasse Persondas Attribut hat age, behauptet er, dass es unmöglich sein sollte, anhand der Notation zu erkennen, ob sie Person.ageintern so etwas wie return current_year - self.birth_dateoder einfach so entspricht return self.age, wo self.agesie als konstantes Attribut definiert wurde. Das macht für mich Sinn. Er behauptet jedoch weiterhin Folgendes:

Die Standard-Client-Dokumentation für eine Klasse, die als Kurzform der Klasse bezeichnet wird, soll nicht offenbaren, ob ein bestimmtes Merkmal ein Attribut oder eine Funktion ist (in Fällen, in denen es sich um eines handeln könnte).

Das heißt, er behauptet, dass selbst in der Dokumentation der Klasse nicht angegeben werden sollte, ob ein "Getter" eine Berechnung durchführt oder nicht.

Diesem folge ich nicht. Ist die Dokumentation nicht der einzige Ort, an dem es wichtig wäre, die Benutzer über diese Unterscheidung zu informieren? Wenn ich eine mit PersonObjekten gefüllte Datenbank entwerfen würde, wäre es dann nicht wichtig zu wissen, ob es sich Person.ageum einen teuren Aufruf handelt oder nicht , damit ich entscheiden könnte, ob eine Art Cache dafür implementiert werden soll oder nicht? Habe ich falsch verstanden, was er sagt, oder ist er nur ein besonders extremes Beispiel für die OOP-Designphilosophie?


1
Interessante Frage. Kürzlich habe ich nach etwas sehr Ähnlichem gefragt: Wie würde ich eine Schnittstelle so gestalten, dass klar ist, welche Eigenschaften ihren Wert ändern können und welche konstant bleiben? . Und ich habe eine gute Antwort bekommen, die auf Dokumentation hindeutet, also genau das, wogegen Bertrand Meyer zu argumentieren scheint.
Stakx

Ich habe das Buch nicht gelesen. Gibt Meyer Beispiele für den von ihm empfohlenen Dokumentationsstil? Es fällt mir schwer, mir vorzustellen, was Sie für eine Sprache beschrieben haben.
user16764

1
@PatrickCollins Ich schlage vor, Sie lesen 'Hinrichtung im Reich der Nomen' und stecken hier hinter dem Konzept der Verben und Nomen. Zweitens geht es bei OOP NICHT um Getter und Setter, ich schlage Alan Kay (Erfinder von OOP) vor: Programmierung und Skalierung
AndreasScheinert

@AndreasScheinert - meinst du das ? Ich kicherte über das "Alles aus Mangel an einem Hufeisennagel", aber es scheint ein Schimpfen über die Übel der objektorientierten Programmierung zu sein.
Patrick Collins

1
@PatrickCollins ja das: steve-yegge.blogspot.com/2006/03/… ! Es gibt einige Punkte zum Nachdenken, die anderen sind: Sie sollten Ihre Objekte in Datenstrukturen umwandeln, indem Sie Setter (ab) verwenden.
AndreasScheinert

Antworten:


58

Ich denke nicht, dass Meyer der Meinung ist, dass Sie dem Benutzer nicht sagen sollten, wann Sie eine teure Operation haben. Wenn Ihre Funktion die Datenbank erreicht oder eine Anfrage an einen Webserver stellt und mehrere Stunden mit dem Rechnen verbringt, muss dies für anderen Code bekannt sein.

Der Programmierer, der Ihre Klasse verwendet, muss jedoch nicht wissen, ob Sie Folgendes implementiert haben:

return currentAge;

oder:

return getCurrentYear() - yearBorn;

Die Leistungsmerkmale zwischen diesen beiden Ansätzen sind so gering, dass es keine Rolle spielen sollte. Dem Programmierer, der Ihre Klasse verwendet, sollte es wirklich egal sein, welche Sie haben. Das ist der Punkt von Meyer.

Angenommen, Sie haben eine Größenmethode für einen Container. Das könnte umgesetzt werden:

return size;

oder

return end_pointer - start_pointer;

oder es könnte sein:

count = 0
for(Node * node = firstNode; node; node = node->next)
{
    count++
}
return count

Der Unterschied zwischen den ersten beiden sollte eigentlich keine Rolle spielen. Aber der letzte könnte schwerwiegende Auswirkungen auf die Leistung haben. Deshalb ist die STL zum Beispiel sagt , dass .size()ist O(1). Es dokumentiert nicht genau, wie die Größe berechnet wird, aber es gibt mir die Leistungsmerkmale.

Also : Leistungsprobleme dokumentieren. Dokumentieren Sie keine Implementierungsdetails. Es ist mir egal, wie std :: sort meine Sachen sortiert, solange es so richtig und effizient ist. Ihre Klasse sollte auch nicht dokumentieren, wie sie Dinge berechnet, aber wenn etwas ein unerwartetes Leistungsprofil aufweist, dokumentieren Sie dies.


4
Außerdem: Dokumentieren Sie zuerst die Komplexität von Zeit und Raum und erklären Sie dann, warum eine Funktion diese Eigenschaften hat. ZB:// O(n) Traverses the entire user list.
Jon Purdy

2
= (Etwas so Triviales wie Python lentut dies nicht ... (Zumindest in einigen Situationen, O(n)wie wir in einem College-Projekt erfahren haben, als ich vorschlug, die Länge zu speichern, anstatt sie bei jeder Schleifeniteration neu zu berechnen)
Izkata,

@ Izkata, neugierig. Erinnern Sie sich, was Struktur war O(n)?
Winston Ewert

@ WinstonEwert leider nicht. Das war vor mehr als 4 Jahren in einem Data Mining-Projekt, und ich hatte es meinem Freund nur vorgeschlagen, weil ich mit C in einer anderen Klasse gearbeitet hatte.
Izkata

1
@ JonPurdy Ich würde hinzufügen, dass es im normalen Geschäftscode wahrscheinlich keinen Sinn macht, die Big-O-Komplexität anzugeben. Zum Beispiel ist ein O (1) -Datenbankzugriff wahrscheinlich viel langsamer als ein Durchlauf der speicherinternen O (n) -Liste. Dokumentieren Sie also, worauf es ankommt. Aber es gibt sicherlich Fälle, in denen die Dokumentation der Komplexität sehr wichtig ist (Sammlungen oder anderer algorithmischer Code).
Svick

16

Aus akademischer oder CS-puristischer Sicht ist es natürlich ein Versäumnis, in der Dokumentation irgendetwas über die Interna der Implementierung eines Features zu beschreiben. Dies liegt daran, dass der Benutzer einer Klasse im Idealfall keine Annahmen über die interne Implementierung der Klasse treffen sollte. Wenn sich die Implementierung ändert, wird dies im Idealfall kein Benutzer bemerken - die Funktion erstellt eine Abstraktion und die Interna sollten vollständig verborgen bleiben.

Allerdings leiden die meisten realen Programme von Joel Spolsky`s „Gesetz der undichten Abstraktionen“ , die besagt ,

"Alle nicht-trivialen Abstraktionen sind bis zu einem gewissen Grad undicht."

Das heißt, es ist praktisch unmöglich, eine vollständige Black-Box-Abstraktion komplexer Features zu erstellen. Ein typisches Symptom hierfür sind Leistungsprobleme. Für reale Programme kann es daher sehr wichtig werden, welche Aufrufe teuer sind und welche nicht, und eine gute Dokumentation sollte diese Informationen enthalten (oder angeben, wo der Benutzer einer Klasse Annahmen über die Leistung treffen darf und wo nicht) ).

Mein Ratschlag lautet daher: Geben Sie Informationen zu potenziell teuren Anrufen an, wenn Sie Dokumente für ein reales Programm schreiben, und schließen Sie sie für ein Programm aus, das Sie nur zu Bildungszwecken für Ihren CS-Kurs schreiben, da Leistungsaspekte berücksichtigt werden sollten absichtlich außerhalb des Anwendungsbereichs.


+1, sowie die meisten der Dokumentation , die erstellt werden , sind für den nächsten Programmierer zu halten Ihr Projekt, nicht der nächste Programmierer verwendet es.
Jmoreno

12

Sie können schreiben, ob ein bestimmter Anruf teuer ist oder nicht. Verwenden Sie besser eine Benennungskonvention wie getAgefür den schnellen Zugriff und / loadAgeoder fetchAgefür die teure Suche. Sie möchten den Benutzer auf jeden Fall informieren, wenn die Methode eine E / A durchführt.

Jedes Detail, das Sie in der Dokumentation angeben, ist wie ein Vertrag, den die Klasse einhalten muss. Es sollte über wichtiges Verhalten informieren. Oft werden Sie Komplexitätsangaben mit großer O-Notation sehen. Aber normalerweise möchten Sie kurz und sachlich sein.


1
+1 für die Erwähnung, dass die Dokumentation ebenso Teil des Vertrags einer Klasse ist wie ihre Schnittstelle.
Bart van Ingen Schenau

Ich unterstütze das. Darüber hinaus wird allgemein versucht, den Bedarf an Gettern zu minimieren, indem Methoden mit Verhalten versehen werden.
sevenforce

9

Wenn ich eine Datenbank mit Personenobjekten entwerfen würde, wäre es dann nicht wichtig zu wissen, ob Person.age ein teurer Anruf ist oder nicht?

Ja.

Aus diesem Grund verwende ich manchmal Find()Funktionen, um anzuzeigen, dass das Aufrufen eine Weile dauern kann. Dies ist mehr eine Konvention als alles andere. Die Zeit, die für eine Funktion oder ein Attribut zu Rückkehr nimmt keinen Unterschied macht , um das Programm (obwohl es den Benutzer, könnte), obwohl unter Programmierern es ist die Erwartung , dass, wenn es als ein Attribut deklariert ist, die Kosten zu nennen sollten niedrig.

In jedem Fall sollte es genügend Informationen im Code selbst geben, um abzuleiten, ob es sich um eine Funktion oder ein Attribut handelt, daher sehe ich die Notwendigkeit, dies in der Dokumentation zu sagen, nicht wirklich.


4
+1: Diese Konvention ist an einigen Stellen idiomatisch. Außerdem sollte die Dokumentation auf der Ebene der Benutzeroberfläche erfolgen. Zu diesem Zeitpunkt wissen Sie nicht , wie Person.Age implementiert ist.
Telastyn

@Telastyn: Ich habe nie so über Dokumentation nachgedacht. Das heißt, dass dies auf der Schnittstellenebene erfolgen sollte. Es scheint jetzt offensichtlich. +1 für diesen wertvollen Kommentar.
Stakx

Ich mag diese Antwort sehr. Ein perfektes Beispiel für das, was Sie beschreiben, dass die Leistung für das Programm selbst kein Problem darstellt, wäre, wenn Person eine von einem RESTful-Service abgerufene Entität wäre. GET ist inhärent, aber es ist nicht ersichtlich, ob dies billig oder teuer sein wird. Dies ist natürlich nicht unbedingt OOP, aber der Punkt ist der gleiche.
maple_shaft

+1 für die Verwendung von GetMethoden über Attributen, um eine schwerere Operation anzuzeigen. Ich habe genug Code gesehen, in dem Entwickler davon ausgehen, dass eine Eigenschaft nur ein Accessor ist und ihn mehrmals verwendet, anstatt den Wert in einer lokalen Variablen zu speichern, und daher einen sehr komplexen Algorithmus mehrmals ausführen. Wenn es keine Konvention gibt, solche Eigenschaften nicht zu implementieren, und die Dokumentation keinen Hinweis auf die Komplexität gibt, dann wünsche ich allen, die eine solche Anwendung warten müssen, viel Glück.
Enzi

Woher kommt diese Konvention? Wenn ich an Java denke, würde ich erwarten, dass es umgekehrt ist: getMethode, die einem Attributzugriff entspricht und daher nicht teuer ist.
sevenforce

3

Es ist wichtig anzumerken, dass die erste Ausgabe dieses Buches 1988 in den Anfängen der OOP geschrieben wurde. Diese Leute arbeiteten mit rein objektorientierten Sprachen, die heute weit verbreitet sind. Unsere derzeit beliebtesten OO-Sprachen - C ++, C # und Java - haben einige ziemlich signifikante Unterschiede zu der Art und Weise, wie die frühen, rein OO-Sprachen funktionierten.

In einer Sprache wie C ++ und Java müssen Sie zwischen dem Zugriff auf ein Attribut und einem Methodenaufruf unterscheiden. Es gibt eine Welt voller Unterschiede zwischen instance.getter_methodund instance.getter_method(). Einer bekommt tatsächlich deinen Wert und der andere nicht.

Wenn Sie mit einer rein OO-Sprache arbeiten, der Smalltalk- oder Ruby-Sprache (die offenbar die in diesem Buch verwendete Eiffel-Sprache ist), wird dies zu einem absolut gültigen Ratschlag. Diese Sprachen rufen implizit Methoden für Sie auf. Es wird kein Unterschied zwischen instance.attributeund instance.getter_method.

Ich würde diesen Punkt nicht schwitzen oder zu dogmatisch sehen. Die Absicht ist gut - Sie möchten nicht, dass sich die Benutzer Ihrer Klasse über irrelevante Implementierungsdetails Gedanken machen - aber es lässt sich nicht sauber in die Syntax vieler moderner Sprachen übersetzen.


1
Sehr wichtiger Punkt zur Berücksichtigung des Jahres, in dem der Vorschlag gemacht wurde. Nit: Smalltalk und Simula stammen aus den 60er und 70er Jahren, so dass 88 kaum "frühe Tage" sind.
Luser Droog

2

Als Benutzer müssen Sie nicht wissen, wie etwas implementiert ist.

Wenn die Leistung ein Problem darstellt, muss innerhalb der Klassenimplementierung etwas getan werden, und nicht darum herum. Daher besteht die richtige Aktion darin, die Klassenimplementierung zu korrigieren oder einen Fehler beim Betreuer einzureichen.


3
Ist es jedoch immer so, dass eine rechenintensive Methode ein Fehler ist? Nehmen wir als einfaches Beispiel an, dass es mir darum geht, die Länge eines Arrays von Zeichenfolgen zu summieren. Intern weiß ich nicht, ob Zeichenfolgen in meiner Sprache im Pascal- oder C-Stil vorliegen. Im ersteren Fall kann ich erwarten, dass meine Längensummierungsschleife abhängig von der Anzahl der Zeichenfolgen eine lineare Zeit benötigt, da Zeichenfolgen ihre Länge "kennen". Ich sollte auch wissen, dass Operationen, die die Länge von Zeichenfolgen ändern, mit einem Overhead verbunden sind, da string.lengthsie bei jeder Änderung neu berechnet werden.
Patrick Collins

3
In letzterem Fall kann ich davon ausgehen, dass die Länge der Zeichenfolge nicht "bekannt" ist, dass die Länge der Summierschleife quadratisch ist (dies hängt sowohl von der Anzahl der Zeichenfolgen als auch von ihrer Länge ab), aber von Operationen, die die Länge ändern Saiten werden billiger. Keine dieser Implementierungen ist falsch und verdient auch keinen Fehlerbericht. Sie erfordern jedoch etwas andere Codierungsstile, um unerwartete Probleme zu vermeiden. Wäre es nicht einfacher, wenn der Benutzer zumindest eine vage Vorstellung davon hätte, was vor sich geht?
Patrick Collins

Wenn Sie also wissen, dass die Zeichenfolgenklasse den C-Stil implementiert, wählen Sie eine Codierungsmethode, die diese Tatsache berücksichtigt. Aber was ist, wenn die nächste Version der Zeichenfolgenklasse die neue Darstellung im Foo-Stil implementiert? Ändern Sie Ihren Code entsprechend oder akzeptieren Sie den Leistungsverlust, der durch falsche Annahmen in Ihrem Code verursacht wird?
Mouviciel

Aha. Die OO-Antwort zu "Wie kann ich die Leistung meines Codes verbessern, wenn ich auf eine bestimmte Implementierung angewiesen bin?" ist "Du kannst nicht." Und die Antwort auf "Mein Code ist langsamer als erwartet, warum?" ist "Es muss neu geschrieben werden." Ist das mehr oder weniger die Idee?
Patrick Collins

2
@PatrickCollins Die OO-Antwort basiert auf Schnittstellen und nicht auf Implementierungen. Verwenden Sie keine Schnittstelle, die keine Leistungsgarantien als Teil der Schnittstellendefinition enthält (wie das Beispiel für C ++ 11 List.size, für das O (1) garantiert ist). Es ist nicht erforderlich, Implementierungsdetails in die Schnittstellendefinition aufzunehmen. Wenn Ihr Code langsamer ist als Sie möchten, gibt es eine andere Antwort, als Sie ihn ändern müssen, um schneller zu sein (nachdem Sie ihn profiliert haben, um Engpässe festzustellen)?
Stonemetal

2

Jede programmiererorientierte Dokumentation, die Programmierer nicht über die Komplexitätskosten von Routinen / Methoden informiert, ist fehlerhaft.

  • Wir streben nach nebenwirkungsfreien Methoden.

  • Wenn die Ausführung eines Verfahrens Zeitkomplexität ausgeführt wurde und / oder Speicher Komplexität außer O(1), in Gedächtnis- oder zeitbeschränkten Umgebungen kann betrachtet werden , um Nebenwirkungen haben .

  • Das Prinzip der geringsten Überraschung wird verletzt, wenn eine Methode etwas völlig Unerwartetes ausführt - in diesem Fall Speicher überfrachten oder CPU-Zeit verschwenden.


1

Ich denke, Sie haben ihn richtig verstanden, aber ich denke auch, dass Sie einen guten Punkt haben. wenn Person.agedas mit einer teuren berechnung umgesetzt wird, dann würde ich das wohl auch gerne in der dokumentation sehen. Es kann den Unterschied zwischen dem wiederholten Aufrufen (wenn es sich um eine kostengünstige Operation handelt) oder dem einmaligen Aufrufen und dem Zwischenspeichern des Werts (wenn es teuer ist) ausmachen. Ich weiß es nicht genau, aber ich denke in diesem Fall könnte Meyer zustimmen, dass eine Warnung in die Dokumentation aufgenommen werden sollte.

Eine andere Möglichkeit, dies zu handhaben, besteht darin, ein neues Attribut einzuführen, dessen Name impliziert, dass möglicherweise eine lange Berechnung stattfindet (z. B. Person.ageCalculatedFromDB) und dann Person.ageeinen Wert zurückgibt, der in der Klasse zwischengespeichert ist. Dies ist jedoch möglicherweise nicht immer angemessen und scheint zu kompliziert Dinge, meiner Meinung nach.


3
Man könnte auch das Argument anführen, dass man, wenn man das agevon a wissen muss Person, die Methode aufrufen sollte, um es trotzdem zu bekommen. Wenn Anrufer anfangen, zu clevere Dinge zu tun, um der Berechnung aus dem Weg zu gehen, laufen sie Gefahr, dass ihre Implementierungen nicht richtig funktionieren, weil sie eine Geburtstagsgrenze überschritten haben. Teure Implementierungen in der Klasse manifestieren sich als Leistungsprobleme, die durch Profilerstellung behoben werden können, und Verbesserungen wie das Zwischenspeichern können in der Klasse durchgeführt werden, wobei alle Aufrufer die Vorteile (und korrekten Ergebnisse) sehen.
Blrfl

1
@Blrfl: na ja, das cachen sollte in der Personklasse gemacht werden, aber ich denke die frage war allgemeiner gedacht und das Person.agewar nur ein beispiel. Es gibt wahrscheinlich einige Fälle, in denen die Auswahl für den Anrufer sinnvoller wäre - vielleicht hat der Angerufene zwei verschiedene Algorithmen, um den gleichen Wert zu berechnen: einen schnellen, aber ungenauen, einen viel langsameren, aber genaueren (3D-Rendering wird als eine Stelle in den Sinn gebracht) wo das passieren kann), und die Dokumentation sollte dies erwähnen.
FrustratedWithFormsDesigner

Zwei Methoden, die unterschiedliche Ergebnisse liefern, stellen einen anderen Anwendungsfall dar, als wenn Sie jedes Mal die gleiche Antwort erwarten.
Blrfl

0

Die Dokumentation für objektorientierte Klassen beinhaltet häufig einen Kompromiss zwischen der Flexibilität für die Betreuer der Klasse, ihren Entwurf zu ändern, und der Möglichkeit für die Konsumenten der Klasse, ihr Potenzial voll auszuschöpfen. Wenn eine unveränderliche Klasse wird eine Reihe von Eigenschaften aufweist , die eine gewisse hat genaue Beziehung zueinander (zB Left, RightundWidthEigenschaften eines mit einem Ganzzahlkoordinatengitter ausgerichteten Rechtecks), kann man die Klasse so entwerfen, dass sie eine beliebige Kombination von zwei Eigenschaften speichert und die dritte berechnet, oder alle drei Eigenschaften speichern. Wenn nichts an der Schnittstelle verdeutlicht, welche Eigenschaften gespeichert sind, kann der Programmierer der Klasse möglicherweise das Design ändern, falls sich dies aus irgendeinem Grund als hilfreich erweisen sollte. Wenn im Gegensatz dazu beispielsweise zwei der Eigenschaften als finalFelder verfügbar gemacht werden und die dritte nicht, müssen zukünftige Versionen der Klasse immer dieselben zwei Eigenschaften als "Basis" verwenden.

Wenn Eigenschaften keine exakte Beziehung haben (z. B. weil sie floatoder doubleeher als int), muss möglicherweise dokumentiert werden, welche Eigenschaften den Wert einer Klasse "definieren". Beispielsweise ist Gleitkomma-Mathematik oft ungenau , obwohl LeftPlus Widthgleich sein soll Right. Nehmen wir beispielsweise an, dass ein Parameter , Rectangleder type Floataccept Leftund Widthas als Konstruktorparameter verwendet, mit Leftas 1234567fund Widthas erstellt wird 1.1f. Die beste floatDarstellung der Summe ist 1234568.125 [die als 1234568.13 angezeigt werden kann]; der nächst kleinere floatwäre 1234568.0. Wenn die Klasse tatsächlich Leftund speichertWidthkann der angegebene Breitenwert ausgegeben werden. Wenn jedoch berechnet , der Konstruktor Rightauf der Basis der übergebenen in Leftund Width, und später berechnet Widthauf der Grundlage Leftund Rightwürde es die Breite berichten und 1.25fnicht als übergebenen in 1.1f.

Bei veränderlichen Klassen kann es noch interessanter sein, da eine Änderung an einem der miteinander verknüpften Werte eine Änderung an mindestens einem anderen impliziert, aber es ist möglicherweise nicht immer klar, welcher. Die „set“ eine einzige Eigenschaft als solche in einigen Fällen kann es am besten sein , Methoden zu müssen, sondern entweder Methoden zB SetLeftAndWidthoder SetLeftAndRightoder auch deutlich machen , welche Eigenschaften festgelegt werden, und die ändern (zB MoveRightEdgeToSetWidth, ChangeWidthToSetLeftEdgeoder MoveShapeToSetRightEdge) .

Manchmal kann es nützlich sein, eine Klasse zu haben, die verfolgt, welche Eigenschaftswerte angegeben und welche aus anderen berechnet wurden. Beispielsweise kann eine "Moment in Zeit" -Klasse eine absolute Zeit, eine Ortszeit und einen Zeitzonenversatz enthalten. Wie bei vielen derartigen Typen kann man mit zwei gegebenen Informationen die dritte berechnen. Wissen wasStück Information wurde berechnet, kann jedoch manchmal wichtig sein. Angenommen, ein Ereignis ist um "17:00 UTC, Zeitzone -5, Ortszeit 12:00 Uhr" aufgetreten, und später wird festgestellt, dass die Zeitzone -6 hätte sein müssen. Wenn bekannt ist, dass die UTC von einem Server aufgezeichnet wurde, sollte die Aufzeichnung auf "18:00 UTC, Zeitzone -6, Ortszeit 12:00 Uhr" korrigiert werden. Wenn jemand die Ortszeit außerhalb der Uhr eingibt, sollte dies "17:00 UTC, Zeitzone -6, Ortszeit 11:00 Uhr" sein. Ohne zu wissen, ob die globale oder die lokale Zeit als "glaubwürdiger" angesehen werden soll, ist es jedoch nicht möglich zu wissen, welche Korrektur angewendet werden soll. Wenn jedoch in der Aufzeichnung festgehalten wird, welche Zeit angegeben wurde, können Änderungen an der Zeitzone diese in Ruhe lassen, während die andere geändert wird.


0

All diese Regeln zum Ausblenden von Informationen in Klassen sind unter der Annahme, dass Sie jemanden unter den Benutzern der Klasse davor schützen müssen, der den Fehler macht, eine Abhängigkeit von der internen Implementierung zu erstellen, durchaus sinnvoll.

Es ist in Ordnung, einen solchen Schutz einzubauen, wenn die Klasse ein solches Publikum hat. Wenn der Benutzer jedoch eine Funktion in Ihrer Klasse aufruft, vertraut er Ihnen das Bankkonto zur Ausführungszeit.

Folgendes sehe ich oft:

  1. Objekte haben ein "modifiziertes" Bit, das besagt, ob sie in gewisser Weise veraltet sind. Einfach genug, aber dann haben sie untergeordnete Objekte, so dass es einfach ist, "modified" eine Funktion zu lassen, die über alle untergeordneten Objekte summiert. Wenn es dann mehrere Ebenen untergeordneter Objekte gibt (die manchmal dasselbe Objekt mehrmals gemeinsam nutzen), können einfache Abrufe der Eigenschaft "modified" einen gesunden Bruchteil der Ausführungszeit in Anspruch nehmen.

  2. Wenn ein Objekt auf irgendeine Weise modifiziert wird, wird angenommen, dass andere Objekte, die in der Software verstreut sind, "benachrichtigt" werden müssen. Dies kann über mehrere Ebenen von Datenstrukturen, Fenstern usw. erfolgen, die von verschiedenen Programmierern geschrieben wurden, und manchmal in unendlichen Rekursionen wiederholt werden, gegen die geschützt werden muss. Selbst wenn alle Autoren dieser Benachrichtigungs-Handler einigermaßen darauf bedacht sind, keine Zeit zu verschwenden, kann die gesamte zusammengesetzte Interaktion einen unvorhergesehenen und schmerzhaft großen Teil der Ausführungszeit in Anspruch nehmen, und die Annahme, dass dies einfach "notwendig" ist, wird munter getroffen.

Also, ich mag es Klassen zu sehen, die eine schöne abstrakte Oberfläche für die Außenwelt darstellen, aber ich mag es, eine Vorstellung davon zu haben, wie sie funktionieren, wenn ich nur verstehe, welche Arbeit sie mir ersparen. Aber darüber hinaus neige ich dazu, das Gefühl zu haben, dass "weniger mehr ist". Die Leute sind so verliebt in Datenstrukturen, dass sie denken, dass mehr besser ist, und wenn ich die Leistung optimiere, ist der universelle Grund für Leistungsprobleme das sklavische Festhalten an aufgeblähten Datenstrukturen, die so aufgebaut sind, wie Menschen unterrichtet werden.

Also mach eine Figur.


0

Durch Hinzufügen von Implementierungsdetails wie "Berechnen oder nicht" oder "Leistungsinfo" wird es schwieriger, Code und Dokument synchron zu halten .

Beispiel:

Wenn Sie eine "leistungsintensive" Methode haben, möchten Sie "teuer" auch für alle Klassen dokumentieren, die diese Methode verwenden? Was, wenn Sie die Implementierung so ändern, dass sie nicht mehr teuer ist? Möchten Sie diese Informationen auch für alle Verbraucher aktualisieren?

Natürlich ist es für einen Code-Betreuer schön, alle wichtigen Informationen aus der Code-Dokumentation zu erhalten, aber ich mag keine Dokumentation, die behauptet, dass etwas nicht mehr gültig ist (nicht mehr mit dem Code synchron).


0

Wie die akzeptierte Antwort zu dem Schluss kommt:

Also: Leistungsprobleme dokumentieren.

und selbst dokumentierte Code wird als besser als Dokumentation folgt daraus , dass der Methodenname soll ungewöhnliche Performance - Ergebnisse angeben.

Also noch Person.agefür, return current_year - self.birth_dateaber wenn die Methode eine Schleife verwendet, um das Alter zu berechnen (ja):Person.calculateAge()

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.