Wenn Sie jetzt den von Ihnen geschriebenen Java -Code optimieren müssen, was würden Sie dann tun? In diesem Artikel führt der Autor vier Methoden ein, die die Systemleistung und die Lesbarkeit der Code verbessern können. Wenn Sie daran interessiert sind, schauen wir uns an.
Unsere üblichen Programmieraufgaben sind nichts anderes, als dieselbe technische Suite auf verschiedene Projekte anzuwenden. In den meisten Fällen können diese Technologien die Ziele erreichen. Einige Projekte erfordern jedoch möglicherweise spezielle Techniken, sodass die Ingenieure eingehend untersuchen müssen, um die einfachsten, aber effektivsten Methoden zu finden. In einem früheren Artikel haben wir vier spezielle Technologien diskutiert, die bei Bedarf verwendet werden können, um eine bessere Java -Software zu erstellen. In diesem Artikel werden wir einige gemeinsame Designstrategien und Zielimplementierungstechniken einführen, die dazu beitragen, gemeinsame Probleme zu lösen, nämlich:
Nur zielgerichtete Optimierung
Verwenden Sie so viel wie möglich für Konstanten
Definieren Sie die Equals () -Methode in der Klasse neu
Verwenden Sie so viel Polymorphismus wie möglich
Es ist erwähnenswert, dass die in diesem Artikel beschriebenen Techniken nicht für alle Fälle anwendbar sind. Wenn und wo diese Technologien verwendet werden sollten, müssen Benutzer sorgfältig in Betracht gezogen werden.
1. Nur zielgerichtete Optimierung durchführen
Große Softwaresysteme müssen sehr besorgt über Leistungsprobleme sein. Obwohl wir hoffen, den effizientesten Code schreiben zu können, haben wir, wenn wir den Code optimieren möchten, keine Ahnung, wie wir beginnen sollen. Beeinflusst beispielsweise der folgende Code die Leistung?
public void processInteGers (Liste <Integer> Integer) {für (Integer Value: Intaging) {für (int i = intagler.size ()-1; i> = 0; i--) {value += ingenters.get (i); }}}Es hängt von der Situation ab. Im obigen Code können wir sehen, dass sein Verarbeitungsalgorithmus o (n³) (mit großen O -Symbolen) ist, wobei n die Größe des Listensatzes hat. Wenn N nur 5 ist, wird es kein Problem geben, nur 25 Iterationen werden durchgeführt. Wenn N jedoch 100.000 beträgt, kann dies die Leistung beeinflussen. Bitte beachten Sie, dass wir trotzdem nicht feststellen können, dass es Probleme geben wird. Obwohl diese Methode 1 Milliarde logische Iterationen erfordert, muss noch diskutiert werden, ob sie sich auf die Leistung auswirken wird.
Angenommen, der Client führt diesen Code in seinem eigenen Thread aus und wartet asynchron, bis die Berechnung abgeschlossen ist, und seine Ausführungszeit kann akzeptabel sein. In ähnlicher Weise müssen wir diesen Code nicht optimieren, da das System in einer Produktionsumgebung, aber kein Client es nennt, die Gesamtleistung des Systems überhaupt nicht konsumiert. Tatsächlich wird das System nach der Optimierung der Leistung komplexer, aber die tragische Sache ist, dass sich die Leistung des Systems dadurch nicht verbessert.
Das Wichtigste ist, dass es in der Welt kein kostenloses Mittagessen gibt. Um die Kosten zu senken, verwenden wir normalerweise Technologien wie Cache, Schleifenerweiterung oder vorbereitete Werte, um eine Optimierung zu erreichen, was wiederum die Komplexität des Systems erhöht und die Lesbarkeit des Codes verringert. Wenn diese Optimierung die Leistung des Systems verbessern kann, lohnt es sich auch dann, wenn es kompliziert wird. Bevor Sie jedoch eine Entscheidung treffen, müssen Sie zuerst diese beiden Informationen kennen:
Was sind die Leistungsanforderungen?
Wo ist der Performance -Engpass?
Zunächst müssen wir klar wissen, was die Leistungsanforderungen sind. Wenn es letztendlich innerhalb der Anforderungen liegt und der Endbenutzer keine Einwände erhöht hat, müssen keine Leistungsoptimierung durchgeführt werden. Wenn jedoch neue Funktionen hinzugefügt werden oder das Datenvolumen des Systems eine bestimmte Skala erreicht, muss es optimiert werden, andernfalls können Probleme auftreten.
In diesem Fall sollte es nicht auf Intuition oder Inspektion beruhen. Denn selbst erfahrene Entwickler wie Martin Fowler sind anfällig für falsche Optimierungen, wie im Artikel Refactoring (Seite 70) erläutert:
Wenn Sie genügend Programme analysieren, finden Sie das Interessante an der Leistung, dass die meiste Zeit in einem kleinen Teil des Codes im System verschwendet wird. Wenn alle Codes gleich optimiert sind, ist das Endergebnis, dass 90% der Optimierung verschwendet werden, da der Code nach der Optimierung nicht viel Frequenz ausführt. Die Zeit für die Optimierung ohne Ziele ist Zeitverschwendung.
Als kattbeschädigter Entwickler sollten wir diese Sichtweise ernst nehmen. Die erste Vermutung ist nicht nur, dass die Leistung des Systems nicht verbessert wurde, sondern 90% der Entwicklungszeit sind vollständig verschwendet. Stattdessen sollten wir gemeinsame Anwendungsfälle in der Produktion (oder Vorproduktion) ausführen und herausfinden, welcher Teil des Systems während der Ausführung Systemressourcen verbraucht, und dann das System konfigurieren. Beispielsweise ist nur 10% des Code, der die meisten Ressourcen verbraucht, und dann die verbleibenden 90% des Codes zu optimieren, ist Zeitverschwendung.
Nach den Analyseergebnissen sollten wir mit den häufigsten Situationen beginnen, wenn wir dieses Wissen nutzen möchten. Da dies sicherstellt, dass der tatsächliche Aufwand letztendlich die Leistung des Systems verbessert. Nach jeder Optimierung sollten die Analyseschritte wiederholt werden. Da dies nicht nur sicherstellt, dass die Leistung des Systems wirklich verbessert ist, ist es auch zu sehen, welcher Teil des Leistungsguts nach der Optimierung des Systems ist (da nach der Lösung eines Engpasses andere Engpässe möglicherweise mehr Gesamtressourcen des Systems konsumieren). Es ist zu beachten, dass der Prozentsatz der Zeit, die für bestehende Engpässe aufgewendet werden, wahrscheinlich zunehmen wird, da die verbleibenden Engpässe vorübergehend unverändert sind und die Gesamtausführungszeit reduziert werden sollte, wenn der Zielgut Engpass beseitigt wird.
Obwohl es viel Kapazität erfordert, um die Profile in Java -Systemen voll zu überprüfen, gibt es einige sehr häufige Tools, die dazu beitragen können, dass Hotspots für Systeme Performance entdeckt werden, einschließlich JMeter, AppDynamics und YourKit. Darüber hinaus können Sie sich auch auf die Leistungsüberwachungshandbuch von Dzone finden Sie für weitere Informationen zur Java -Programmleistungoptimierung.
Obwohl die Leistung ein sehr wichtiger Bestandteil vieler großer Softwaresysteme ist und Teil der automatisierten Testsuite in der Produktlieferungspipeline ist, kann sie nicht blind und ohne Zweck optimiert werden. Stattdessen sollten spezifische Optimierungen für die gemeisterten Leistungsgpässe vorgenommen werden. Dies hilft uns nicht nur, die Komplexität des Systems zu erhöhen, sondern ermöglicht es uns auch, Umwege zu vermeiden und zeitraubende Optimierungen durchzuführen.
2. Versuchen Sie, Enums für Konstanten zu verwenden
Es gibt viele Szenarien, in denen Benutzer eine Reihe vordefinierter oder konstanter Werte auflisten müssen, z. B. HTTP -Antwortcodes, die möglicherweise in Webanwendungen auftreten. Eine der häufigsten Implementierungstechniken besteht darin, eine neue Klasse zu erstellen, die viele statische Endtypwerte enthält. Jeder Wert sollte einen Kommentar haben, der beschreibt, was der Wert bedeutet:
public class httpresponsecodes {public static final int ok = 200; public static final int Not_found = 404; public static final int bustbidden = 403;} if (gethttPesponse (). getStatusCode () == httpresponsecodes.ok) {// etwas tun, wenn der Antwortcode in Ordnung ist}Es ist schon sehr gut, diese Idee zu haben, aber es gibt immer noch einige Nachteile:
Keine strenge Überprüfung der eingehenden Ganzzahlwerte
Da es sich um einen grundlegenden Datentyp handelt, kann die Methode im Statuscode nicht aufgerufen werden
Im ersten Fall wird einfach eine bestimmte Konstante erstellt, um einen speziellen Ganzzahlwert darzustellen, aber es gibt keine Einschränkung der Methode oder Variablen, sodass der verwendete Wert möglicherweise über den Umfang der Definition hinausgeht. Zum Beispiel:
public class httpesponseHandler {public static void printMessage (int statuscode) {System.out.println ("ergebener Status von" + StatusCode); }} HttPresponseHandler.printMessage (15000);Obwohl 15000 kein gültiger HTTP -Antwortcode sind, gibt es auf der Serverseite keine Einschränkung, dass der Client gültige Ganzzahlen bereitstellen muss. Im zweiten Fall haben wir keine Möglichkeit, eine Methode für den Statuscode zu definieren. Wenn Sie beispielsweise überprüfen möchten, ob ein bestimmter Statuscode ein erfolgreicher Code ist, müssen Sie eine separate Funktion definieren:
public class httpresponsecodes {public static final int ok = 200; public static final int Not_found = 404; öffentliches statisches Finale int verboten = 403; public static boolean issuccess (int statuscode) {return statusCode> = 200 && statuscode <300; }} if (httpresponsecodes.issuccess (GethttPesponse (). getStatusCode ())) {// etwas tun, wenn der Antwortcode ein Erfolgscode ist}Um diese Probleme zu lösen, müssen wir den konstanten Typ vom Basisdatentyp in einen benutzerdefinierten Typ ändern und nur bestimmte Objekte der benutzerdefinierten Klasse zulassen. Genau dafür sind Java Enums da. Mit Enum können wir diese beiden Probleme gleichzeitig lösen:
public enum httpresponsecodes {OK (200), Forbidden (403), Not_Found (404); privater Final INT -Code; Httpresponsecodes (int code) {this.code = code; } public int getcode () {return code; } public boolean issuccess () {return code> = 200 && code <300; }} if (gethttPesponse (). getStatusCode (). issuccess ()) {// etwas tun, wenn der Antwortcode ein Erfolgscode ist}In ähnlicher Weise ist es nun möglich, dass der Statuscode, der beim Aufrufen der Methode gültig ist, gültig sein muss:
public class httpesponseHandler {public static void printMessage (httpresponsecode statusCode) {System.out.println ("ergebener Status von" + statusCode.getCode ()); }} HttPresponseHandler.printMessage (httpresponsecode.ok);Es ist erwähnenswert, dass dieses Beispiel zeigt, dass Sie, wenn es eine Konstante ist, versuchen sollten, Enums zu verwenden, aber es bedeutet nicht, dass Sie Enums unter allen Umständen verwenden sollten. In einigen Fällen kann es wünschenswert sein, eine Konstante zu verwenden, um einen bestimmten Wert darzustellen, aber auch andere Werte sind zulässig. Zum Beispiel kann jeder etwas über PI Bescheid wissen, und wir können eine Konstante verwenden, um diesen Wert zu erfassen (und ihn wiederverwenden):
public class numericConstants {public static Final Double Pi = 3,14; public static Final Double Unit_circle_area = pi * pi;} Public Class Teppich {private endgültige Doppelfläche; öffentliche Klasse Run (Doppelbereich) {this.Area = örtlich; } public double getCost () {Return Area * 2; }} // Erstellen Sie einen Teppich mit einem Durchmesser von 4 Fuß (Radius von 2 Fuß) Teppich Fourfootrug = neuer Teppich (2 * numericConstants.Unit_circle_area);Daher können die Regeln für die Verwendung von Enums als:
Wenn alle möglichen diskreten Werte im Voraus bekannt sind, können Sie die Aufzählung verwenden
Nehmen Sie den oben genannten HTTP -Antwortcode als Beispiel. Möglicherweise kennen wir alle Werte des HTTP -Statuscodes (können in RFC 7231 gefunden werden, das das HTTP 1.1 -Protokoll definiert). Daher wird die Aufzählung verwendet. Bei der Berechnung von PI kennen wir nicht alle möglichen Werte über PI (jedes mögliche Doppel ist gültig), aber gleichzeitig möchten wir eine Konstante für die kreisförmigen Teppiche erstellen, um die Berechnung zu erleichtern (leichter zu lesen). Daher ist eine Reihe von Konstanten definiert.
Wenn Sie nicht alle möglichen Werte im Voraus kennen, aber für jeden Wert Felder oder Methoden einbeziehen möchten, besteht die einfachste Möglichkeit, eine neue Klasse für die Darstellung der Daten zu erstellen. Obwohl ich nie gesagt habe, dass es in keinem Szenario Aufzählung geben sollte, ist der Schlüssel zu wissen, wo und wann nicht die Aufzählung verwendet werden muss, um alle Werte im Voraus bekannt zu machen und die Verwendung eines anderen Werts zu verbieten.
3. Definieren Sie die Equals () -Methode in der Klasse neu
Die Objekterkennung kann ein schwieriges Problem sein, das zu lösen ist: Wenn zwei Objekte die gleiche Position im Speicher einnehmen, sind sie dann gleich? Wenn ihre IDs gleich sind, sind sie gleich? Oder was ist, wenn alle Felder gleich sind? Obwohl jede Klasse eine eigene Identifikationslogik hat, gibt es viele westliche Länder in dem System, die beurteilen müssen, ob sie gleich sind. Zum Beispiel gibt es eine Klasse unten, die den Bestellanschluss angibt ...
öffentliche Klasse Kauf {private long id; public long getid () {return id; } public void setId (Long id) {this.id = id; }}... Wie unten geschrieben, müssen in dem Code viele Orte enthalten, die ähnlich sind:
Originalpurchase = new Cup (); kauf updatedPurchase = new Cups (); if (originalpurchase.getId () == UpdatatedPurchase.getId ()) {// Führen Sie eine Logik für gleiche Einkäufe aus} aus}Je mehr diese Logikanrufe (im Gegenzug, es verstößt gegen das Trockenprinzip), Kauf
Die Identitätsinformationen der Klasse werden auch immer mehr. Wenn aus irgendeinem Grund der Kauf geändert wurde
Die Identitätslogik einer Klasse (z. B. wurde die Art der Kennung geändert), sodass es viele Orte geben muss, an denen die Identitätslogik aktualisiert wird.
Wir sollten diese Logik in der Klasse initialisieren, anstatt die Identitätslogik der Kaufklasse zu stark über das System zu verbreiten. Auf den ersten Blick können wir eine neue Methode erstellen, z. B. Issame, deren Einschlussparameter ein Kaufobjekt ist, und die IDs jedes Objekts vergleichen, um festzustellen, ob sie gleich sind:
öffentliche Klasse Kauf {private long id; public boolean issame (andere) {return getId () == other.gerid (); }}Obwohl dies eine effektive Lösung ist, wird die integrierte Funktionalität von Java ignoriert: Verwendung der Equals-Methode. Jede Klasse in Java erbt die Objektklasse, obwohl sie implizit ist, so dass sie auch die Equals -Methode erbt. Standardmäßig überprüft diese Methode die Objektidentität (gleiches Objekt im Speicher), wie im folgenden Code -Snippet in der Objektklassendefinition (Version 1.8.0_131) in JDK gezeigt:
public boolean gleich (Objekt obj) {return (this == obj);}Diese gleiche Methode wirkt als natürlicher Ort für die Injektion der Identitätslogik (implementiert durch Überschreibung des Standards):
öffentliche Klasse Kauf {private long id; public long getid () {return id; } public void setId (Long id) {this.id = id; } @Override public boolean Equals (Objekt Andere) {if (this == other) {return true; } else if (! (Andere Instanz des Kaufs)) {return false; } else {return ((kaufen) andere) .getId () == getId (); }}}Obwohl diese Equals -Methode kompliziert aussieht, müssen wir nur drei Fälle berücksichtigen, da die Equals -Methode nur Parameter von Typobjekten akzeptiert:
Ein anderes Objekt ist das aktuelle Objekt (d. H. Originalpurchase.equals (OriginalPurchase)). Per Definition sind sie das gleiche Objekt. Geben Sie also True zurück
Das andere Objekt ist kein Kaufobjekt, in diesem Fall können wir die Kauf -ID nicht vergleichen, sodass die beiden Objekte nicht gleich sind
Andere Objekte sind nicht dasselbe Objekt, sondern Kaufinstanzen. Es hängt daher davon ab, ob die aktuelle Kauf -ID und einen anderen Kauf gleich sind. Jetzt können wir unsere früheren Bedingungen wie folgt neu umrüsten:
Originalpurchase = new buy (); kaufen updatedPurchase = new Cups (); if (originalpurchase.equals (aktualisiertPurchase)) {// Führen Sie eine Logik für gleiche Einkäufe aus} aus}Zusätzlich zur Reduzierung der Replikation im System hat die Refactoring der Standard -Equals -Methode einige andere Vorteile. Wenn wir beispielsweise eine Liste von Kaufobjekten erstellen und überprüfen, ob die Liste ein anderes Kaufobjekt mit derselben ID (verschiedene Objekte im Speicher) enthält, erhalten wir einen echten Wert, da die beiden Werte als gleich angesehen werden:
LIST <Kauf> Einkäufe = Neue ArrayList <> (); Käufe.Add (Originalpurchase); Einkäufe.Contains (aktualisiertPurchase); // WAHR
Egal, wo Sie sich befinden, wenn Sie feststellen müssen, ob die beiden Klassen gleich sind, müssen Sie nur die umgeschriebene Equals -Methode verwenden. Wenn wir die Equals -Methode implizit anwenden möchten, weil das Objekt zur Beurteilung der Gleichheit verurteilt wird, können wir wie folgt auch den == Operator verwenden:
if (originalpurchase == UpdatedPurchase) {// Die beiden Objekte sind dieselben Objekte im Speicher}}Es sollte auch beachtet werden, dass die HashCode -Methode auch nach der Umschrift der Equals -Methode neu geschrieben werden sollte. Weitere Informationen zur Beziehung zwischen diesen beiden Methoden und der korrekten Definition von HashCode
Methode siehe diesen Thread.
Wie wir gesehen haben, initalisiert das Überschreiben der Equals-Methode nicht nur die Identitätslogik in der Klasse, sondern reduziert auch die Ausbreitung dieser Logik im gesamten System, sondern ermöglicht es der Java-Sprache auch, gut informierte Entscheidungen über die Klasse zu treffen.
4. Verwenden Sie so weit wie möglich Polymorphismen
Für jede Programmiersprache sind bedingte Sätze eine sehr häufige Struktur, und es gibt bestimmte Gründe für ihre Existenz. Da verschiedene Kombinationen es dem Benutzer ermöglichen können, das Verhalten des Systems basierend auf dem angegebenen Wert oder dem sofortigen Zustand des Objekts zu ändern. Unter der Annahme, dass der Benutzer den Restbetrag jedes Bankkontos berechnen muss, kann der folgende Code entwickelt werden:
public enum bankAcountType {girschen, sparen, zertifikat_of_deposit;} öffentliche Klasse BankAccount {private endgültige BankAccountType -Typ; public bankAccount (BankAccountType -Typ) {this.type = Typ; } public double getInterestrate () {Switch (Typ) {Fallüberprüfung: return 0.03; // 3% Falleinsparungen: Return 0,04; // 4% Case Certificate_of_deposit: return 0.05; // 5% Standard: Neue nicht unterstützte OperationException (); }} public boolean SupportSDepoSsits () {Switch (Typ) {case Checking: return true; Falleinsparung: Return True; Case Certificate_of_deposit: false zurückgeben; Standard: Wirf eine neue nicht unterstützte OperationException (); }}}Obwohl der obige Code die grundlegenden Anforderungen erfüllt, gibt es einen offensichtlichen Fehler: Der Benutzer bestimmt nur das Verhalten des Systems basierend auf dem Typ des angegebenen Kontos. Dies erfordert nicht nur, dass Benutzer den Kontotyp überprüfen, bevor sie eine Entscheidung treffen, sondern diese Logik auch bei einer Entscheidung wiederholen müssen. Im obigen Design muss der Benutzer beispielsweise beide Methoden einchecken. Dies kann zu außer Kontrolle geraten, insbesondere wenn Sie einen neuen Kontotyp hinzufügen müssen.
Wir können Polymorphismus verwenden, um Entscheidungen implizit zu treffen, anstatt Kontotypen zu verwenden, um sie zu unterscheiden. Dazu wandeln wir die konkreten Klassen von BankAccount in eine Schnittstelle um und geben den Entscheidungsprozess in eine Reihe konkreter Klassen über, die jede Art von Bankkonto darstellen:
/*** Java -Lern- und Kommunikations -QQ -Gruppe: 589809992 Lass uns zusammen Java lernen! */public interface bankAccount {public double getInterestrate (); public boolean Support Deposits ();} Public Class Checking Account Implements BankAccount {@Override public double getestrate () {return 0.03; } @Override public boolean Support Deposits () {return true; }} public class SavingsAccount implementiert BankAccount {@Override public double getInTestrate () {return 0,04; } @Override public boolean unterstütztdeposis () {return true; }} public class CertificateOfDePositAccount implementiert BankAccount {@Override public double getestRate () {return 0,05; } @Override public boolean assitDeposis () {return false; }}Dies verkauft nicht nur Informationen, die für jedes Konto in eine eigene Klasse spezifisch sind, sondern unterstützt auch Benutzer, ihre Designs auf zwei wichtige Weise zu ändern. Erstens, wenn Sie einen neuen Bankkontotyp hinzufügen möchten, müssen Sie nur eine neue bestimmte Klasse erstellen, die BankAccount -Schnittstelle implementieren und die spezifische Implementierung der beiden Methoden angeben. Bei der Konstruktion des bedingten Struktur müssen wir dem Enum einen neuen Wert hinzufügen, in beiden Methoden eine neue Fallanweisung hinzufügen und die Logik des neuen Kontos in jeder Fallerklärung einfügen.
Zweitens müssen wir nur eine neue Methode in jeder Betonklasse hinzufügen, wenn wir eine neue Methode in der BankAccount -Schnittstelle hinzufügen möchten. Im bedingten Design müssen wir die vorhandene Switch -Anweisung kopieren und zu unserer neuen Methode hinzufügen. Zusätzlich müssen wir jeweils die Logik für jeden Kontotyp hinzufügen.
Wenn wir mathematisch eine neue Methode erstellen oder einen neuen Typ hinzufügen, müssen wir die gleiche Anzahl logischer Änderungen im polymorphen und bedingten Design vornehmen. Wenn wir beispielsweise eine neue Methode in einem polymorphen Design hinzufügen, müssen wir den Betonklassen aller N -Bankkonten die neue Methode hinzufügen, und in einem bedingten Design müssen wir in unserer neuen Methode neue Fallanweisungen hinzufügen. Wenn wir im polymorphen Design einen neuen Konto -Typ hinzufügen, müssen wir alle M -Nummern in der BankAccount -Schnittstelle implementieren. In der bedingten Entwurf müssen wir jeder m vorhandenen Methode eine neue Fallerklärung hinzufügen.
Obwohl die Anzahl der Änderungen, die wir vornehmen müssen, gleich ist, ist die Art der Veränderungen völlig anders. Wenn wir im polymorphen Design einen neuen Kontotyp hinzufügen und vergessen, eine Methode aufzunehmen, macht der Compiler einen Fehler, da wir nicht alle Methoden in unserer BankAccount -Schnittstelle implementieren. Im bedingten Design gibt es keine solche Überprüfung, um sicherzustellen, dass jeder Typ eine Fallanweisung hat. Wenn ein neuer Typ hinzugefügt wird, können wir einfach vergessen, jede Switch -Anweisung zu aktualisieren. Je ernsthafter dieses Problem ist, desto mehr wiederholen wir unsere Switch -Anweisung. Wir sind Menschen und wir neigen dazu, Fehler zu machen. Jedes Mal, wenn wir uns auf den Compiler verlassen können, um uns an Fehler zu erinnern, sollten wir dies tun.
Die zweite wichtige Note dieser beiden Entwürfe ist, dass sie extern gleich sind. Wenn wir beispielsweise den Zinssatz für ein Girokonto überprüfen möchten, sieht das bedingte Design so aus:
BankAccount Cheunt Account = New BankAccount (BankAccountType.Checking); System.out.println (CheckingAccount.getInterestrate ()); // Ausgabe: 0,03
Stattdessen ähneln polymorphe Designs wie folgt:
BankAccount CheckAccount = new CheckingAccount (); System.out.println (CheckingAccount.getInterestrate ()); // Ausgabe: 0,03
Aus externer Sicht rufen wir nur GetInterEunk () im BankAccount -Objekt an. Dies wird noch offensichtlicher sein, wenn wir den Kreationsprozess in eine Fabrikklasse abstrakten:
public class ConditionalAccountFactory {public static BankAccount CreateChingAccount () {return New BankAccount (BankAccountType.Checking); }} öffentliche Klasse polymorphicAccountFactory {public static bankAccount createChingAccount () {return New CheckingAccount (); }} // In beiden Fällen erstellen wir die Konten mit einem FactoryBankAccount ConditionalCheckingAccount = conditionalAccountFactory.CreateCeckingAccount (); bankaccount polymorphicChingAccount = polymorphicountfactory.createChingAccount (); // In beiden Fällen, in beiden Fällen, wird der Anspruch auf die Anforderungen, die der Preis zu erhalten, den Anspruch auf den Preis zu erhalten, den der Preiserguss zur Verfügung stellt. samesystem.out.println (ConditionalCheckingAccount.getInterestrate ()); // Ausgabe: 0.03System.out.println (polymorphicChingAccount.getInterestrate ()); // Ausgabe: 0,03Es ist sehr häufig, die bedingte Logik durch polymorphe Klassen zu ersetzen, sodass Methoden veröffentlicht wurden, um bedingte Aussagen in polymorphe Klassen zu rekonstruieren. Hier ist ein einfaches Beispiel. Darüber hinaus beschreibt Martin Fowlers Refactoring (S. 255) den detaillierten Prozess der Durchführung dieser Rekonstruktion.
Wie andere Techniken in diesem Artikel gibt es keine harte und schnelle Regel darüber, wann ein Übergang von der bedingten Logik zu polymorphen Klassen durchgeführt werden soll. Tatsächlich empfehlen wir nicht, es in irgendeiner Situation zu verwenden. In einem testgetriebenen Design: Zum Beispiel entwarf Kent Beck ein einfaches Währungssystem mit dem Ziel, polymorphe Klassen zu verwenden. Erfahrung und angemessene Beurteilung bestimmen, wann der richtige Zeitpunkt für die Umwandlung des bedingten Codes in den polymorphen Code umgewandelt wird.
Abschluss
Als Programmierer sollten wir, obwohl die in normalen Zeiten verwendeten herkömmlichen Techniken die meisten Probleme lösen können, manchmal diese Routine durchbrechen und aktiv etwas Innovation fordern. Als Entwickler erlaubt es uns, als Entwickler die Breite und Tiefe seines Wissens zu erweitern, nicht nur intelligenteren Entscheidungen, sondern auch schlauer.