[Hausaufgaben 2.0] Zusammenfassung der Hausaufgaben von OO Unit 2

Alle Hausaufgaben in der zweiten Einheit von OO sind erledigt. Das Thema dieser Hausaufgaben ist Multithread-Programmierung. Ich werde den Inhalt der Aufgabe hier analysieren und reflektieren.

1. Multithread-Koordination und synchrone Steuerung

In den drei Aufgaben ist mein Entwurf im Wesentlichen derselbe, und der Entwurf von "Hauptthread + Planungsthread + Eingabethread + mehrere Aufzugsthreads" wird übernommen. Die spezifischen Inhalte lauten wie folgt:

1. Die erste Aufgabe

Für den ersten Job gab es nur einen Aufzug. Ich habe Elevator, Dispatcher und MainClass erstellt, um verwandte Arbeiten abzuschließen. Unter anderem:

  • Die MainClass-Klasse ist nur für die Erstellung und den Thread-Start der Dispatcher-Klasse verantwortlich.
  • Die Funktionen der Dispatcher-Klasse lauten wie folgt:
    1. Erstellen und starten Sie einen Aufzugsprozess.
    2. Erstellen Sie ein Eingabeobjekt und warten Sie auf die Eingabe. Beenden Sie den eigenen Prozess am Ende der Eingabe.
    3. Wenn die Anfrage empfangen wird, wird die Anfrage an den Aufzug gesendet.
  • Die Funktionen der Elevator-Klasse sind wie folgt:
    1. Verbinden Sie die Anfrage aktiv von der Warteschlange bis zum Aufzug
    2. Senden Sie die Anfrage an die Zieletage

Wie in der obigen Abbildung gezeigt, repräsentiert im Aufzug das Stockwerk das Stockwerk, doorOpen den Status des Aufzugstürschalters, die Richtung die Fahrtrichtung des Aufzugs, wobei 1-> nach oben, -1-> nach unten, 0-> anhalten
(dies kann sich auf dem Boden befinden Verwenden Sie beim Ändern direkt die Richtung Etage + =, um die Bedienlogik zu vereinfachen.

Verwenden Sie zwei Warteschlangen, floorRequest und CabinetRequest, um die Anforderung zu speichern, wobei floorRequest eine gemeinsam genutzte Variable mit der Dispatcher-Klasse ist und angibt, dass alle Etagen auf Aufzugsanforderungen warten. Die CabinRequest ist eine Anfrage, die den Aufzug betreten hat. FloorRequest ist eine typische gemeinsam genutzte Variable. Der Dispatcher kann darauf schreiben, Elevator kann lesen oder schreiben und muss zum Schutz gesperrt werden.

Meine Aufzugsplanung verwendet den LOOK-Algorithmus. Wenn sich eine Anforderung in der FloorRequest-Warteschlange befindet, wird die Richtung der ersten angeforderten Abflugetage als Richtung ausgewählt. Wenn der Aufzug zu einer bestimmten Etage fährt, prüfen Sie zunächst, ob eine Anforderung zum Starten von der aktuellen Etage in der Etage vorliegt. Wenn dies der Fall ist, fügen Sie diese in die eigene Kabine ein. Anforderung (Anforderung zum Betreten des Aufzugs). Überprüfen Sie, ob eine Anforderung zum Erreichen der aktuellen Etage in der Kabinenanforderung vorliegt Verschieben Sie es dann aus der Warteschlange (Anforderung abgeschlossen) und entscheiden Sie, ob die Tür in der aktuellen Etage gemäß der oben beschriebenen Situation beim Betreten und Verlassen des Aufzugs geöffnet werden soll. Wenn nach der aktuellen Richtung in cabRequest keine Anforderung für die Ankunft des Stockwerks vorliegt und nach der aktuellen Richtung in floorRequest keine Anforderung für die Abfahrt des Stockwerks vorliegt, hält der Aufzug an (Richtung = 0) und die nächste Richtung wird beurteilt.

Der Aufzug fährt als Hauptzyklus eine Ebene nach oben oder unten. Wenn Sie eine Schleife (Abfrage) fortsetzen, wenn keine Anforderung vorliegt, überschreitet die CPU-Zeit den Standard und es tritt ein CTLE-Fehler auf. Daher beende ich den aktuellen Etagenvorgang im Aufzug und gehe in die nächste Etage, um ein Urteil zu fällen. Wenn beide Warteschlangen von cabRequest und floorRequest leer sind, gehe ich in den Wartezustand und warte auf Eingabe. Hier gibt es einen kleinen Fehler. Nach dem Hinzufügen einer neuen Anfrage oder wenn der Thread endet, ist es leicht zu erkennen, dass der Aufzug feststeckt. Nach der Analyse habe ich den Grund gefunden.

Im ursprünglichen Design wurde nach dem Aufwecken des Elevator-Threads sofort geprüft, ob sich eine Anforderung in der Warteschlange befand. Zu diesem Zeitpunkt hat Elevator die neue Anforderung möglicherweise nicht erkannt und wartete erneut. Ich habe nach dem Ende der Wartezeit eine Wartezeit von 1 ms hinzugefügt und darauf gewartet, dass der Dispatcher die Anforderung vollständig eingibt, bevor ich mit der nächsten Durchquerung fortfahre, wodurch das Problem behoben wurde.

Wie in der obigen Abbildung gezeigt, gibt es in der Dispatcher-Klasse eine Warteschlange für floorRequest (für die Iteration reserviert, von allen Aufzügen gemeinsam genutzt), eine Aufzugswarteschlange für Aufzüge (wie viele Aufzüge gibt es nur einen für den ersten Job) und eine Eingabeaufforderungsliste für inputOver (tatsächlich verwendet) Eine benutzerdefinierte Klasse kann als Sperre am Ende der Eingabe verwendet werden. Der Container wird hier verwendet, um mehrere Eingaben in nachfolgenden Operationen vorzubereiten. Nachfolgende Iterationen beweisen jedoch, dass ein solches Design tatsächlich nicht funktioniert. Nachdem der Dispatcher-Thread gestartet wurde, startet er zuerst den Aufzugsthread, den er besitzt, und ist dann für das Hinzufügen der Eingabeanforderung zur floorRequest verantwortlich. Fügen Sie nach Abschluss der Eingabe ein echtes Element in inputOver ein, um anzuzeigen, dass die Eingabe abgeschlossen ist.

Die Dispatcher-Klasse im ersten Job spielte eine weniger praktische Rolle. Als ich über iteratives Design nachdachte, wollte ich, dass der Aufzug die Anfrage selbst erhält. Dies ist etwas unerwünscht, da der Dispatcher-Thread und der Elevator-Thread zu wenige Aufgaben haben und keine ausgewogene Funktionsverteilung erreichen.

Außerdem habe ich ziemlich lange gebraucht, um den Thread zu beenden. Im ursprünglichen Entwurf habe ich Dispatcher notifyAll-Aufzüge am Ende der Eingabe erstellt, aber dieser Entwurf hat ein großes Problem: Der Aufzugsprozess befindet sich möglicherweise im laufenden Zustand oder im Wartezustand am Ende der Eingabe, und diese Statusinformationen können nicht im Dispatcher enthalten sein Direkt vom Thread abrufen. Wenn der Aufzug noch ausgeführt wird, benachrichtigt der Dispatcher All. Wenn der Aufzug die Verarbeitung der Anforderung beendet hat, wartet er erneut und blockiert auf unbestimmte Zeit. Um dieses Problem zu lösen, habe ich den Shared Signal InputOver verwendet, um es zu lösen.

Wenn Elevator nach der Verarbeitung der Anforderung beurteilt, ob in inputOver kein echtes Element vorhanden ist, bedeutet dies, dass die Eingabe nicht beendet wurde, in den Wartezustand wechselt und auf die Eingabeanforderung wartet. Wenn in inputOver ein echtes Element vorhanden ist, bedeutet dies, dass die Eingabe beendet wurde, und wenn die gesamte Verarbeitung beendet ist Anfrage, der Aufzug sollte den Vorgang direkt beenden.

Schließlich besteht ein Hauptproblem bei dieser Zuweisung darin, dass ich fast die gesamte Geschäftslogik in die Ausführungsmethode eingefügt habe. Diese Art der Verarbeitungsmethode ist eine schlechte Angewohnheit, egal ob es sich um objektorientierte oder prozessorientierte Programmierung handelt. Der Hauptgrund für den Fehler ist, dass ich nicht genug Verständnis für Multithread-Programmierung habe und nicht sicher bin, ob andere asynchrone Methoden als die Ausführungsmethode verarbeitet werden. Diese Probleme wurden im zweiten und dritten Arbeitsgang verbessert.

2. Der zweite Job

Die zweite Operation wird hauptsächlich in drei Aspekten erweitert:

  1. Die Anzahl der Aufzüge stieg auf 5
  2. Liftlimit 7 Personen
  3. Aufzug hat negativen Boden

Für Anforderung 1 hatte ich es während der ersten Operation erwartet, ändern Sie einfach die Anzahl der Aufzüge in Dispatcher.

Für Bedarf 2 hat sich die Verarbeitungsmethode von Bedarf 2 stark geändert, solange der Aufzug nicht vollständig mit neuen Anforderungen beladen ist, wenn er vollständig beladen ist. Nachdem ich jedoch die Architektur geändert habe, hat er zwar effiziente Ergebnisse erzielt Ich habe den Fehler nicht erwartet.

Für Bedarf 3 gibt es tatsächlich kein Problem mit negativen Etagen. Der Typ int unterstützt bereits negative Zahlen, es sollte jedoch beachtet werden, dass keine Anweisung "0 Etage" vorhanden ist, sodass eine spezielle Behandlung erforderlich ist. Die intuitivste Lösung besteht darin, in der aktuellen Richtung weiter zu steigen oder zu fallen, wenn Sie auf die 0. Etage treffen. Meine verbesserte Verarbeitungsmethode für Bedarf 2 muss jedoch den Floor-Unterschied vorhersagen. Direkte Subtraktion führt zu einem Fehler des 0. Stocks. Wenn mehr Logik eingeführt wird, enthält das Verfahren viel unnötigen Code. Daher habe ich die Methode zum Zuordnen der Etagen von -3 -1, 1 15 und der Indizes von 0 ~ 17 übernommen. Der Vorteil dieses Prozesses besteht darin, dass, wenn andere spezielle Etagen verbunden werden müssen (z. B. Überspringen, Überspringen von 4) Und der 13. Stock, der "G" -Boden existiert an speziellen Orten wie Krankenhäusern, die umgekehrte Definition des Schiffsbodens, der höchste ist der erste Stock und das Deck ist der achte Stock.) Die Art der Boden- und Indexzuordnung kann verschiedene Anforderungen und die Skalierbarkeit lösen Ok.

Darüber hinaus habe ich in Bezug auf die Fahrstuhlabfertigung einige Änderungen vorgenommen und bin der Vorhersage der nachfolgenden Iterationen im ersten Auftrag nicht vollständig gefolgt. Der Autor stellte fest, dass bei der ersten Operation des Programms "Self-Request-Anfrage für Aufzüge" mehrere Aufzüge als "Wettbewerb um Fahrgäste" auftreten. Dies ist kein Thread-Sicherheitsproblem, sondern das unvermeidliche Problem, das durch das Teilen der FloorRequest-Warteschlange entsteht Problem. Obwohl dieses Design die Korrektheit nicht zerstört und die Leistung nicht zu stark beeinträchtigt, ist es ernsthaft inkonsistent mit dem tatsächlichen Szenario des Aufzugsversands. Im wirklichen Leben wird es nie einen Aufzug geben. Nach dem Drücken der Taste reagieren mehrere Leerlaufaufzüge gleichzeitig (weil der unnötige Stromverbrauch erhöht wird). Darüber hinaus hat diese Konstruktion das Problem der "Aufzugskonvergenz" in der frühen Phase des Aufzugsbetriebs, dh mehrere Aufzüge bewegen sich gleichzeitig auf mehreren benachbarten Stockwerken und haben dieselbe Bewegungsrichtung. Dieses Design ist nicht elegant und weist keine technische Qualität auf. Der Autor verwendet den LOOK-Algorithmus für die Aufzugsabfertigung, um den Entwurf genau auf die Leistungsanforderungen abzustimmen und das Problem aus der realen Szene heraus zu lösen, da die reale und häufig verwendete Aufzugsversandstrategie das Ergebnis eines Langzeittests sein muss . Also habe ich folgende Änderungen vorgenommen:

Ich trenne die FloorRequest-Warteschlange, jeder Aufzug hat eine FloorRequest-Warteschlange, die die Anforderungswarteschlange ist, die auf den Aufzug wartet, und der Dispatcher fügt eine waitRequest-Warteschlange hinzu, bei der es sich um eine nicht zugewiesene Anforderung handelt. Der Dispatcher-Thread ist dafür verantwortlich, je nach Betrieb jedes Aufzugs unterschiedliche Anforderungen zu verteilen. Das Zuweisen ist diesmal eine Verbesserung meines Hausaufgabenalgorithmus, hat aber auch einige Thread-Sicherheitsfehler verursacht. Nach der Änderung können diese Fehler vollständig beseitigt werden, nicht das Problem des Algorithmus selbst. Gemessen an den bestandenen Stichprobenwerten ist diese Planungsleistung immer noch sehr hoch.

Der Aufzug, der den LOOK-Algorithmus verwendet, folgt hauptsächlich dem Prinzip "Versuch, die Richtung nicht zu ändern", kombiniert mit den inhärenten Vorteilen des "Ziel-Bodenauswahl-Aufzugs". Wir können die Anzahl der Passagiere zu jedem Zeitpunkt ausführen, zu dem der Aufzug vorwärts wechselt, bevor wir aufgefordert werden, den Aufzug zu betreten Zur Vorhersage habe ich das Array "EstimationNum" verwendet, um die Anzahl der Personen auf jeder Etage jeder Fahrt mit dem Aufzug zu speichern. Wenn die vorhergesagte Anzahl der Personen 7 überschreitet oder die aktuelle Anforderung in die entgegengesetzte Richtung des Aufzugs läuft oder der Aufzug die angeforderte Abfahrtsetage verpasst hat, sendet der Dispatcher den aktuellen nicht mehr Die Etage des Aufzugs wird der Anforderung hinzugefügt und verwendet andere Aufzüge. Andere Anforderungen sollten im selben Aufzug enthalten sein. Diese Art der Planung kann einen einzelnen Aufzug bei wenigen Anforderungen so stark wie möglich überlasten, wodurch der Stromverbrauch des Aufzugs gespart wird. Bei vielen Anforderungen kann jeder Aufzug versetzt werden, um ihn auf verschiedenen Etagen aktiv zu machen.

Diese Änderung verursachte jedoch auch einen schwerwiegenden Fehler im zweiten Job. Da ich mich vollständig auf EstimateNum verlasse, um die Aufzugslast zu begrenzen, gibt es keine obligatorische Begrenzung für die Anzahl der Personen. Wenn der Aufzug rückwärts fährt, werden den an EstimationNum vorgenommenen Änderungen neue Anforderungen hinzugefügt. Es wird nach der Kommutierung gelöscht, was die versteckte Gefahr einer Überlastung mit sich bringt. Der wesentliche Grund für dieses Problem besteht darin, dass das Hinzufügen von Aufzugsanforderungen und das Ändern von EstimationNum nicht vollständig synchronisiert werden können (kein Problem der Thread-Synchronisierung), da die Voraussetzungen für die beiden Vorgänge unterschiedlich sind und nicht zusammen ausgeführt werden können. Wenn Sie diesen Fehler beheben möchten, müssen Sie nur die Anzahl der Aufzüge erzwingen. Wenn der Aufzug vollständig beladen ist, verhindert der Aufzugs-Thread, dass eine Anforderung in den Aufzug gelangt. Obwohl dies dem ursprünglichen Design widerspricht, weist es eine hohe Sicherheit auf.

Die Thread-Stop-Strategie für diesen Job wurde ebenfalls entsprechend geändert. Um den Betriebsstatus jedes Aufzugs zu erhalten, habe ich den inputOver konzentriert und ob der Aufzug in einer gemeinsam genutzten Klasse namens MainLock ausgeführt wird (weil diese Klasseninstanz Als gemeinsam genutzte Variable spielt sie hauptsächlich die Rolle einer Sperre. Die Signale, dass die Eingabe abgeschlossen ist und der Aufzug stoppt, werden durch diese Klasse übertragen (siehe Abbildung). Die synchronisierte Methode wurde jedoch beim Entwerfen dieser Klasse nicht verwendet, aber der Aufrufer musste die Thread-Sicherheit von MainLock gewährleisten. Dies ist in der Tat unangemessen. Ich denke über diesen Punkt nach und sollte entsprechende Änderungen vornehmen.

Gleichzeitig habe ich die Elevator.run () -Methode dieses Jobs vereinfacht, nur die Logik der obersten Ebene platziert und die ursprünglichen Aufzugsstufen in verschiedene Methoden unterteilt. Obwohl dies etwas prozessorientiert ist, ist die Hierarchie klarer. Außerdem ist das Aufzugsverhalten unter meinem Planungsalgorithmus offensichtlich periodisch und fest. Einige prozessorientierte Verarbeitungen sind nicht unbedingt schlecht (natürlich gibt es Raum für Verbesserungen).

3. Die dritte Hausaufgabe

Diese Operation setzt im Wesentlichen die Praxis der zweiten Einheit im Multithread-Betrieb fort, aber die Dispatcher-Methode verfügt über mehr Logik, um den Aufzug dynamisch zu verbinden und zu starten. Das spezifische Funktionsdesign und das Leistungsdesign werden im zweiten Teil erörtert.

Zweitens das Design und die Skalierbarkeit des dritten Jobs

1. Verbesserungen des Klassendesigns

In der dritten Aufgabe habe ich folgende Änderungen vorgenommen:

  1. Die InputPanel-Klasse wird hinzugefügt, um die Aufgabe der Eingabeanforderung von der geplanten Aufgabe zu trennen, die MainLock-Klasse wird abgebrochen und das Beendigungsrecht des Aufzugsthreads wird vollständig an den Dispatcher übergeben.
  2. Elevator wird in eine abstrakte Klasse geändert, die von drei Unterklassen von ElevatorA, ElevatorB und ElevatorC geerbt wird. Die Hauptaufgabe der Vererbung besteht darin, Informationen wie Geschwindigkeit, Last und verfügbare Stockwerke zu überschreiben.
  3. Fügen Sie die von der PersonRequest-Klasse geerbte CommuteRequest-Klasse hinzu. Um die Aufzugsübertragungsanforderung zu erfüllen, wird die zu übertragende Anforderung in Unteranforderungen aufgeteilt, die während der Eingabeverarbeitungsphase nicht übertragen werden müssen.

2. Balance zwischen funktionalem Design und Leistungsdesign

Ziehen Sie Lehren aus schwerwiegenden Fehlern im zweiten Job. Beim dritten Job stelle ich die Richtigkeit und Planungsstabilität an die erste Stelle. Aus den Merkmalen der abrufbaren Stockwerke der drei ABC-Aufzüge ist ersichtlich, dass mindestens eine Übertragung zwischen zwei beliebigen Stockwerken erforderlich ist. Das erste und das 15. Stockwerk sind die Stockwerke, in denen die drei Aufzüge parken können. Basierend auf diesen Eigenschaften entwerfe ich den Planungsalgorithmus wie folgt:

  1. Direkte Anfragen werden so weit wie möglich direkt verteilt. Wenn mehrere erreichbare Optionen vorhanden sind, lautet die Priorität A> B> C (unter Berücksichtigung der Laufzeit). Wenn mehrere Aufzüge verfügbar sind, wird der Planungsalgorithmus für den zweiten Auftrag (korrigiert) zwischen mehreren Aufzügen derselben Art verwendet .
  2. Die nicht erreichbare Anforderung wird in zwei direkte Anforderungen aufgeteilt, und die Etage mit dem kürzesten Gesamtabstand zwischen der 1. und 15. Etage wird als Transferetage ausgewählt.
  3. Da die Aufzugsressourcen in der dritten Operation relativ fest sind, wird die Zuweisung von Aufzugsanforderungen durch "Kommen von" dominiert, und die Vermeidung von Überlastung wird durch den Aufzugs-Thread implementiert.

Die Ergebnisse der drei Tests lauten wie folgt: das erste Mal: ​​96,9113, das zweite Mal: ​​58,8578, das dritte Mal: ​​90,5247. Unter ihnen wurden alle starken Testproben zum ersten und dritten Mal und 12/20 zum zweiten Mal bestanden. Die bestandenen Stichprobenwerte sind alle größer als 97.

Daraus ist ersichtlich, dass die Leistung: zweites Mal> erstes Mal> drittes Mal, Stabilität: drittes Mal = erstes Mal >> zweites Mal. Die Ergebnisse der konservativen Strategie, die im dritten Auftrag angenommen wurde, sind für mich recht zufriedenstellend. Ich denke, der größte Verbesserungsbedarf liegt in der Wahl des Transferbodens. Wenn solche Verbesserungen vorgenommen werden sollen, muss die CommuteRequest-Klasse verschiedene Aufzugsinformationen abrufen, und die Generierung von Unteranforderungen wird komplizierter. Die dadurch verursachten Sicherheitsrisiken sollten jedoch viel geringer sein als diejenigen, die durch den Vorhersagealgorithmus für die Überlastung des Aufzugs im zweiten Arbeitsgang verursacht werden.

3. Betrachten Sie die dritte Aufgabe aus der Perspektive der Gestaltungsprinzipien

SOLID 之 SRP (Prinzip der Einzelverantwortung)

Dieses Prinzip erfordert, dass jede Methode nur eine eindeutige Verantwortung trägt. Ich denke, ich habe es in der Dispatcher-Klasse und der CommuteRequest-Klasse besser gemacht. Die Funktion jeder Methode ist relativ einfach und klar. Es gibt jedoch einige Probleme mit den Klassen InputPanel und Elevator.

Die Funktion der InputPanel-Klasse selbst ist sehr einfach. Rufen Sie einfach die entsprechende Anforderung ab. Das Hauptproblem besteht darin, dass die gesamte Geschäftslogik zur run () -Methode hinzugefügt wird. Dieser Code sieht immer noch präzise aus, lässt jedoch die run-Methode nicht die logischen Eigenschaften der obersten Ebene dieser Klasse widerspiegeln. Hier sollten Verbesserungen vorgenommen werden.

SOLID 之 OCP (Open-Close-Prinzip)

Das Prinzip des Öffnens und Schließens habe ich in den Iterationen dieser beiden Einheitenoperationen erreicht. Ich muss bei jeder Iteration entsprechende Änderungen und Anpassungen am vorhandenen Code vornehmen. Ich denke, der Hauptgrund für diese Art von Problem ist nicht das Versäumnis, eine gute Vorhersage der nachfolgenden Nachfrage zu treffen. Schließlich werden die Zunahme der Anzahl der Aufzüge und die Belastungsgrenze alle im ersten Betrieb erwartet. Das eigentliche Problem ist das In der vorherigen Arbeit gab es ein faules Verhalten in der Architektur, und die Schritte waren nicht vollständig getrennt, dh das SRP-Prinzip konnte nicht vollständig umgesetzt werden, was zu der Schwierigkeit führte, das OCP-Prinzip umzusetzen. Dies sollte in der zukünftigen Arbeit berücksichtigt werden.

FESTER LSP, ISP, DIP

Die Operationen dieser Einheit haben keine komplizierten Vererbungs- und Implementierungsbeziehungen, und die Implementierung auf Klassenebene ist relativ einfach. Daher spiegeln sich das Liskov-Prinzip und das Schnittstellenkapselungsprinzip nicht wider. Jedes Modul hat seine eigenen Aufgaben, es gibt keine offensichtliche Zugehörigkeit, und ich habe nicht festgestellt, dass sich das DIP-Prinzip in der Arbeit dieser Einheit klar widerspiegeln kann.

Andere Gestaltungsprinzipien

  • Balance-Prinzip: Hier habe ich mich während des Iterationsprozesses verbessert. Die Dispatcher-Klasse ist nur für das Hinzufügen von Eingaben zur Warteschlange vom ersten Job bis zum zweiten Mal verantwortlich, um Warteanforderungen bestimmten Aufzügen zuzuweisen, und die dritte Verarbeitung ist anwendbar Anfragen für verschiedene Aufzüge.
  • Lokalisierungsprinzip: Dieses Prinzip spiegelt sich in der Vererbung von Elevator wider. Die drei Klassen von ElevatorA / B / C überschreiben nur ihre spezifische Geschwindigkeit, Kapazität, erreichbare Stockwerke und andere Informationen, und es gibt keine Code-Redundanz.
  • Das Prinzip des expliziten Ausdrucks: Es gibt Probleme bei der Anzeige und dem Ausdruck der Arbeit dieser Einheit. Beispielsweise verwendet die Richtung Zahlen für die Definition der Richtung anstelle von logischen Definitionen wie isGoingUp und isGoingDown. Dies vereinfacht die Berechnungslogik, ist jedoch selbst bei ausreichenden Anmerkungen nicht offensichtlich genug. In einem Mehrpersonenprojekt sollte dieses Problem vermieden werden.
  • Verstehe meine Prinzipien: Meine Variablennamen sind selten faul und alle folgen der englischen vollständigen Nomenklatur + vollständiger Name und Kamelbuckel, die klar und leicht zu verstehen ist.

4. Skalierbarkeit

Die dritte Zuweisung ist bereits das Ende der Iteration im OO-Kurs, es ist jedoch weiterhin erforderlich, die Skalierbarkeit beizubehalten. Die folgenden skalierbaren Versuche wurden in meinen Hausaufgaben gemacht:

  1. Die Kontrolle über den Aufzugsprozess liegt vollständig beim Dispatcher. Wenn später eine Anforderung zum Stoppen des Aufzugs hinzugefügt wird, kann sichergestellt werden, dass der Dispatcher den Aufzug rechtzeitig anhalten kann.
  2. Die Zuordnungsbeziehung zwischen Etage und Index floor2Index, index2Floor. Dies wurde bereits im Multithread-Einführungsteil des zweiten Jobs erwähnt, damit verschiedene komplexe Bodenbenennungen und -verteilungen verarbeitet werden können.
  3. Führen Sie die CommuteRequest-Klasse ein, um die PersonRequest-Anforderung aufzuteilen und komplexere Anforderungen (z. B. Roundtrip-Anforderungen) zu verarbeiten.

3. Programmstruktur basierend auf Metrikanalyse

1. Anzahl der Klassenattribute, Anzahl der Methoden, Gesamtgröße des Klassencodes

Erste Hausaufgaben

Zweiter Job

Dritte Hausaufgabe

Es ist ersichtlich, dass vom ersten bis zum dritten Job die Anzahl der Attribute und Methoden jeder Klasse allmählich ausgeglichen wird, aber es gibt immer noch Probleme mit den großen Elevator- und Dispatcher-Methoden und den kleinen inputPanel- und MainClass-Methoden. Wie kann dieses Problem verbessert werden? Es lohnt sich zu diskutieren. Ich denke, wir sollten erwägen, MainClass eine neue Logik hinzuzufügen, wenn umfassendere Anforderungen auftreten, und der Iterationsraum von InputPanel ist ebenfalls sehr groß.

2. Maßstab jeder Methode, Kontrolle jeder Methode, Anzahl der Zweige

Erste Hausaufgaben

Zweiter Job

Dritte Hausaufgabe

In der ersten Operation ist deutlich zu erkennen, dass die Elevator.run-Methode zu groß ist und 46 Zeilen erreicht, was andere Methoden bei weitem übertrifft. In der zweiten Operation wurde dieses Problem verbessert, die längste Methodenzeile wurde auf 26 Zeilen reduziert und die Anzahl der Methodenzeilen wurde durchschnittlich. Die checkRequest-Methode in der dritten Operation ist etwas größer. Nach der Überprüfung wurde festgestellt, dass diese Methode hauptsächlich darauf zurückzuführen ist, dass die verschiedenen Aufzugstypen eine Schalterstruktur zur Klassifizierung verwenden, was zu einer sehr langen Menge an Code führt, die nicht effektiv verwendet wird. Wenn die Arten von Aufzügen weiter zunehmen, können Sie verschiedene checkRequest-Methoden für verschiedene Aufzüge konfigurieren.

3. Klassische OO-Metriken: Methodenkomplexität

Erste Hausaufgaben

Zweiter Job

Dritte Hausaufgabe

Ähnlich wie das Problem, das sich in der Codegröße widerspiegelt, weist die Methodenkomplexität auch darauf hin, dass die Komplexität der Elevator.run-Methode im ersten Job zu hoch ist, der Code nicht gut verteilt ist und der zweite und dritte Job selten sind Die Komplexität wird rot angezeigt und die Komplexität bleibt ähnlich.

4. Klassendiagramm

Erste Hausaufgaben

Zweiter Job

Dritte Hausaufgabe

(Um das Klassendiagramm klarer zu machen, habe ich die Methoden und Attribute weggelassen)

Die Struktur dieser Einheit ist grundsätzlich ähnlich, außer dass die dritte Einheit Aufzugstypen und abstrakte Klassen hinzufügt und CommuteRequest hinzufügt. Es gibt keinen großen Unterschied. Alle verwenden die Struktur "Dispatcher (Scheduler) + Elevator (Aufzug)".

Die Vorteile dieser Struktur sind folgende: 1. Die Struktur ist einfach und klar, und es ist einfach, den Planungsalgorithmus zu ändern. 2. Der Dispatcher hat die volle Kontrolle über den Aufzug und ist in Bezug auf die Gewindesicherheit relativ stabil

Als ich iterative Arbeit leistete, entdeckte ich natürlich auch die Mängel dieser Struktur: Funktionen sind auf die aktuellen Anforderungen beschränkt, und einige Funktionen sind immer noch stark gekoppelt, was der weiteren iterativen Entwicklung nicht förderlich ist.

5. UML-Kollaborationsdiagramm

Erste Hausaufgaben

Zweiter Job

Dritte Hausaufgabe

Schreiben Sie am Ende

Alles in allem ist der Abschluss der Hausaufgaben in dieser Einheit im Vergleich zur vorherigen Einheit rückständig. Ich sollte die Probleme in meinen Hausaufgaben rechtzeitig reflektieren und lösen und das Auftreten ähnlicher Probleme in Zukunft vermeiden. Komm schon, Einheit Drei!

Ich denke du magst

Origin www.cnblogs.com/RenJierui/p/12727163.html
Empfohlen
Rangfolge