Letzte Woche habe ich Ihnen eine Einführung in den GPT-4-Mathematikunterricht geschickt. Viele Menschen waren schockiert und hatten das Gefühl, dass dies eine Revolution in der Bildungsbranche auslösen könnte.
Wie schlägt es sich im Programmierbereich? Lassen Sie uns nicht darüber reden, ob es Programmierer ersetzen kann. Es bedarf noch weiterer Tests und Rückmeldungen. Ich möchte zunächst versuchen, ob es Programmieren wie Mathematik lehren kann.
Ich habe ein Konzept gefunden, das in der Java-Programmierung schwer zu verstehen ist: dynamischer Proxy, und habe es getestet.
Unten ist das komplette Gespräch, das ich damit geführt habe, wobei ich vorgab, ein Neuling zu sein, der Java-Programmierung lernt und das Proxy-Muster nicht kannte, um zu sehen, wie es mir beigebracht wurde.
Der Inhalt ist etwas lang. Es wird empfohlen, ihn geduldig zu lesen und die Fähigkeiten der KI zu spüren.
Wer es wirklich nicht aushält, kann bis zum Ende springen und sich die Highlights direkt ansehen.
[Liu Xin]: Angenommen, Sie sind ein Mentor, der Fragen immer im sokratischen Stil beantwortet. Du wirst mir die Antwort nicht direkt geben, aber du wirst mich zum Nachdenken anregen, okay?
[GPT]: Natürlich. Wenn Sie Fragen haben, lassen Sie sich von mir zum Nachdenken und Diskutieren anleiten.
[Liu Gemeinsamer Ansatz von Socrates zur Erforschung dynamischer Java-Proxys. Zuerst müssen wir das Proxy-Muster verstehen. Was wissen Sie über das Agenturmodell? Was ist der Zweck des Proxy-Musters?
[Liu Xin]: Bedeutet das, dass eine Person Dinge für eine andere Person tut?
[GPT]: Ja, in gewisser Weise geht es beim Proxy-Muster darum, ähnliche Konzepte im Software-Design zu implementieren. In diesem Fall führt ein Objekt (der Agent) eine bestimmte Aufgabe im Namen eines anderen Objekts (des Zielobjekts) aus. Denken Sie nun darüber nach, warum wir möglicherweise das Proxy-Muster bei der Programmierung verwenden müssen? In welchen Situationen wäre die Verwendung des Proxy-Modus hilfreich?
[Liu Das Proxy-Muster ist in vielen Szenarien nützlich, beispielsweise wenn wir vor oder nach dem Zugriff auf ein Objekt einige zusätzliche Vorgänge ausführen müssen. Beispielsweise können wir das Proxy-Muster in den folgenden Situationen verwenden:
Zugriff auf das Zielobjekt steuern (Zugriffskontrolle)
Zusätzliche Funktionalität (z. B. Protokollierung) vor und hinzufügen nachdem das Zielobjekt Vorgänge (Aufzeichnen, Zwischenspeichern usw.) ausführt. Wie kommen also Ihrer Meinung nach dynamische Proxys in diesen Szenarien ins Spiel? Was ist der Unterschied zwischen dynamischen Proxys und statischen Proxys?
[Liu Beispiel.
Angenommen, Sie haben eine Anwendung, die eine Verbindung zu einem Remote-Server herstellen muss, um Daten abzurufen. In diesem Fall können Sie eine Klasse namens RemoteServerConnection erstellen, die für den Verbindungsaufbau mit dem Remote-Server und den Datenabruf verantwortlich ist.
Jetzt muss Ihre Anwendung einige zusätzliche Aktionen ausführen, z. B. die Überprüfung des Netzwerkstatus vor der Verbindung mit dem Remote-Server und die Protokollierung nach dem Abrufen der Daten. Zu diesem Zeitpunkt können Sie den Proxy-Modus verwenden.
Sie können eine Klasse namens RemoteServerConnectionProxy erstellen, die dieselbe Schnittstelle wie RemoteServerConnection hat, aber die Funktionalität zur Überprüfung des Netzwerkstatus und zur Protokollierung hinzufügt. Die Anwendung verwendet RemoteServerConnectionProxy, um Daten abzurufen, anstatt RemoteServerConnection direkt zu verwenden. Der RemoteServerConnectionProxy übernimmt zusätzliche Vorgänge und kommuniziert dann bei Bedarf im Namen der Anwendung mit der RemoteServerConnection.
In diesem Beispiel ist RemoteServerConnectionProxy ein Proxy, der RemoteServerConnection (Zielobjekt) darstellt, um bestimmte Aufgaben auszuführen.
Da Sie nun ein grundlegendes Verständnis des Proxy-Modells haben, schauen wir uns den dynamischen Proxy an. Im obigen Beispiel haben wir eine separate Proxy-Klasse (statischer Proxy) erstellt. Im Gegensatz zu statischen Proxys werden dynamische Proxys zur Laufzeit dynamisch generiert und nicht im Voraus geschrieben. Dynamische Agenten können flexibler auf verschiedene Szenarien reagieren.
In Java werden dynamische Proxys normalerweise durch Implementierung der InvocationHandler-Schnittstelle und Verwendung der Proxy-Klasse implementiert. Denken Sie also darüber nach, warum dynamische Proxys in manchen Situationen Vorteile gegenüber statischen Proxys haben könnten.
[Liu
RemoteServerConnectionProxy fügt der vorherigen Klasse einige Funktionen zum Überprüfen des Netzwerkstatus und zum Aufzeichnen von Protokollen hinzu, aber warum hat es dieselbe Schnittstelle wie RemoteServerConnection?
[GPT]: Tut mir leid, dass ich es vorher nicht klar erklärt habe. Sehen wir uns den Zweck des Proxy-Musters noch einmal an: Ein Objekt (der Proxy) führt eine bestimmte Aufgabe im Namen eines anderen Objekts (des Zielobjekts) aus. Damit das Proxy-Objekt erfolgreich Aufgaben im Namen des Zielobjekts ausführen kann, müssen sie über dieselbe Schnittstelle verfügen. Auf diese Weise ist es dem Client-Code egal, ob er mit dem Proxy-Objekt oder dem Zielobjekt interagiert, da beide über dieselbe Schnittstelle verfügen.
In unserem Beispiel haben RemoteServerConnectionProxy (Proxy-Objekt) und RemoteServerConnection (Zielobjekt) die gleiche Schnittstelle, da die Anwendung diese beiden Klassen so transparent nutzen kann. Wenn wir uns für die Verwendung eines Proxys in unserer Anwendung entscheiden, müssen wir den Client-Code nicht ändern, da sowohl das Proxy-Objekt als auch das Zielobjekt dieselbe Schnittstelle implementieren. Aus diesem Grund müssen das Proxy-Objekt und das Zielobjekt über dieselbe Schnittstelle verfügen.
Darüber hinaus bedeutet die gleiche Schnittstelle, dass das Proxy-Objekt bei der Ausführung von Aufgaben die Methoden des Zielobjekts aufrufen kann. Dies ist wichtig für die Implementierung zusätzlicher Funktionen wie Zugriffskontrolle, Protokollierung usw., da das Proxy-Objekt zum richtigen Zeitpunkt die Kontrolle an das Zielobjekt übergeben muss.
[Liu Xin]: Verstanden, das ist der Proxy-Modus. Was ist also ein dynamischer Proxy?
[GPT]: Großartig, da Sie nun den Proxy-Modus verstanden haben, fahren wir mit dem dynamischen Proxy fort.
Der Hauptunterschied zwischen dynamischen und statischen Proxys besteht darin, wie sie erstellt werden. Statische Proxys sind vorab geschriebene Proxy-Klassen, was bedeutet, dass Sie für jede Zielklasse eine separate Proxy-Klasse erstellen müssen. Dynamische Proxys werden jedoch zur Laufzeit dynamisch generiert, sodass Sie generische Proxys für mehrere Klassen erstellen können, ohne den Quellcode zu ändern. Auf diese Weise reduzieren Sie die Codeduplizierung und verbessern die Wartbarkeit.
In Java wird der dynamische Proxy hauptsächlich über die Klasse java.lang.reflect.Proxy und die Schnittstelle java.lang.reflect.InvocationHandler implementiert. Im Folgenden sind die grundlegenden Schritte eines dynamischen Proxys aufgeführt:
Definieren Sie eine InvocationHandler-Implementierungsklasse, die für die Verarbeitung von Methodenaufrufen für Proxy-Objekte verantwortlich ist. In dieser Klasse können Sie bei Bedarf zusätzliche Vorgänge hinzufügen, z. B. Protokollierung, Zugriffskontrolle usw.
Verwenden Sie die Methode Proxy.newProxyInstance(), um einen dynamischen Proxy zu erstellen. Diese Methode empfängt drei Parameter: Klassenlader, Schnittstellenarray (die Schnittstelle, die die Proxy-Klasse implementieren muss) und InvocationHandler-Instanz. Diese Methode gibt ein Proxy-Objekt zurück, das die angegebene Schnittstelle implementiert.
Verwenden Sie im Client-Code das erstellte dynamische Proxy-Objekt, um Methoden aufzurufen. Wenn Sie eine Methode für das Proxy-Objekt aufrufen, leitet es den Aufruf an die invoke()-Methode der InvocationHandler-Instanz weiter.
Auf diese Weise können Sie zur Laufzeit dynamische Proxys für eine oder mehrere Klassen erstellen, ohne für jede Klasse eine separate Proxy-Klasse schreiben zu müssen.
Da Sie nun das Konzept dynamischer Proxys verstanden haben, denken Sie bitte über die Vorteile und Einschränkungen dynamischer Proxys im Vergleich zu statischen Proxys nach.
[Liu
Angenommen, wir haben eine Bankschnittstelle, die über eine Methode transfer() zur Durchführung von Übertragungsvorgängen verfügt. Wir möchten dieser Schnittstelle Protokollierungsfunktionen hinzufügen, aber die Implementierung der Bank-Schnittstelle nicht ändern. Zu diesem Zeitpunkt können wir einen dynamischen Proxy verwenden.
Zuerst müssen wir einen InvocationHandler implementieren. Diese Klasse ist für die Verarbeitung von Methodenaufrufen des Proxy-Objekts und das Hinzufügen von Protokollierungsfunktionen verantwortlich:
public class LoggingHandler implements InvocationHandler { private Object target; public LoggingHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("日志记录:开始执行 " + method.getName() + " 方法"); Object result = method.invoke(target, args); System.out.println("日志记录:执行 " + method.getName() + " 方法完成"); return result; } }
Bank realBank = new RealBank(); // 假设RealBank是Bank接口的一个实现 InvocationHandler handler = new LoggingHandler(realBank); Bank proxyBank = (Bank) Proxy.newProxyInstance( realBank.getClass().getClassLoader(), realBank.getClass().getInterfaces(), handler );
proxyBank.transfer();
Ich hoffe, dieses Beispiel hilft Ihnen, das Konzept des dynamischen Proxys zu verstehen. Wenn Sie noch Fragen haben, können Sie diese gerne stellen.
[Liu Xin]: In Ihrem Beispiel hoffen wir, der Bankschnittstelle eine Protokollierungsfunktion hinzuzufügen. Dann kann ich einfach einen BankProxy schreiben und den Protokollierungscode hinzufügen? Warum sollte man sich die Mühe machen, InvocationHandler zu verwenden?
[GPT]: Du hast eine sehr gute Frage gestellt. In einigen Fällen kann die Verwendung eines statischen Proxys (wie des von Ihnen erwähnten BankProxy) tatsächlich die gewünschte Funktionalität erreichen. In komplexeren Szenarien können dynamische Proxys jedoch einige erhebliche Vorteile bringen:
Vielseitigkeit: Dynamische Proxys können Proxys für mehrere Klassen und Schnittstellen erstellen, ohne dass für jede Klasse separate Proxy-Klassen geschrieben werden müssen. Wenn Sie die gleiche Funktionalität zu vielen Schnittstellen hinzufügen müssen, z. B. Protokollierung, Zugriffskontrolle usw., können dynamische Proxys Ihnen dabei helfen, doppelten Code zu reduzieren und die Wiederverwendbarkeit des Codes zu verbessern.
Einfache Wartung: Wenn Sie die Funktionalität des Proxys in Zukunft ändern oder hinzufügen müssen, ist es einfacher, einen dynamischen Proxy zu verwenden. Bei statischen Proxys müssen Sie jede Proxy-Klasse einzeln ändern. Bei dynamischen Proxys müssen Sie nur die InvocationHandler-Implementierung ändern. Auf diese Weise können Sie das Verhalten des Agenten einfach ändern, ohne die ursprünglichen Klassen und Schnittstellen zu ändern.
Entkopplung: Dynamische Proxys helfen dabei, Proxy-Logik von Geschäftslogik zu trennen. Wenn Sie die Proxy-Logik ändern müssen, müssen Sie die ursprüngliche Klasse und Schnittstelle nicht ändern. Dadurch ist der Code leichter zu verstehen und zu warten.
Dennoch sind dynamische Proxys nicht immer die beste Lösung. In manchen Fällen ist ein statischer Proxy möglicherweise besser für Ihre Anforderungen geeignet. Wenn Sie sich für die Verwendung eines dynamischen Proxys oder eines statischen Proxys entscheiden, müssen Sie die Vor- und Nachteile auf der Grundlage spezifischer Szenarien und Anforderungen abwägen.
Ich hoffe, diese Antwort kann Ihnen helfen, den Unterschied zwischen dynamischem Proxy und statischem Proxy besser zu verstehen. Wenn Sie weitere Fragen haben, können Sie diese gerne stellen.
[Liu Xin]: Verstanden, aber warum müssen Sie über Proxy.newProxyInstance eine neue Klasse erstellen? Ist es nicht gut, die ursprüngliche RealBank-Klasse direkt zu ändern? [GPT]: Ihre Frage betrifft die Konzepte des Codedesigns und der Entkopplung. Es ist sicherlich möglich, die RealBank-Klasse direkt zu ändern, um Protokollierungsfunktionen hinzuzufügen, aber dies kann zu folgenden Problemen führen:
Single-Responsibility-Prinzip: Gemäß dem Single-Responsibility-Prinzip sollte eine Klasse nur für eine klare Funktion verantwortlich sein. Die Hauptaufgabe der RealBank-Klasse besteht darin, die Bank-Schnittstelle zu implementieren und bankbezogene Geschäftslogik zu verwalten. Wenn wir der RealBank-Klasse Protokollierungsfunktionen hinzufügen, ist diese sowohl für die Geschäftslogik als auch für die Protokollierung verantwortlich, was möglicherweise gegen das Prinzip der Einzelverantwortung verstößt. Durch die Verwendung von Proxys können wir diese beiden Verantwortlichkeiten trennen, sodass sich jede Klasse auf eine klare Funktion konzentriert.
Entkopplung: Wenn wir die Protokollierungsfunktion direkt zur RealBank-Klasse hinzufügen, müssen wir die Implementierung der RealBank-Klasse ändern, wenn wir die Protokollierungsfunktion in Zukunft ändern oder entfernen müssen. Dies kann dazu führen, dass die RealBank-Klasse eng mit der Protokollierungsfunktionalität verknüpft ist, was die Wartbarkeit des Codes verringert. Durch die Verwendung eines Proxys können wir die Protokollierungsfunktion von der RealBank-Klasse trennen, sodass wir die RealBank-Klasse nicht ändern müssen, wenn wir die Protokollierungsfunktion ändern oder entfernen müssen.
Wiederverwendbarkeit: Angenommen, wir möchten mehreren Klassen Protokollierungsfunktionen hinzufügen. Wenn wir die Implementierung jeder Klasse direkt ändern, führt dies zu einer großen Codeduplizierung. Durch die Verwendung von Proxys (insbesondere dynamischen Proxys) können wir gemeinsame Protokollierungsfunktionen für mehrere Klassen bereitstellen, wodurch die Codeduplizierung reduziert und die Wiederverwendbarkeit des Codes verbessert wird.
Kurz gesagt, durch die Verwendung von Proxys können wir dem Prinzip der Einzelverantwortung folgen, eine Entkopplung erreichen und die Wiederverwendbarkeit des Codes verbessern. Während es in einigen Fällen möglich ist, die ursprüngliche Klasse direkt zu ändern, gibt es viele Szenarien, in denen die Verwendung eines Proxys zu einem besseren Codedesign und einer besseren Wartbarkeit führen kann.
Ich hoffe, diese Antwort ist hilfreich für Sie. Wenn Sie weitere Fragen haben, können Sie diese gerne stellen.
[Liu Xin]: Die Parameter von Proxy.newProxyInstance sind so kompliziert, dass ich den
InvocationHandler übergeben kann. Warum müssen wir die anderen beiden Parameter übergeben?
[GPT]: Die Proxy.newProxyInstance-Methode erfordert drei Parameter. Lassen Sie mich ihre Rolle im Detail erklären:
ClassLoader: Der Klassenlader ist für das Laden von Bytecode in die Java Virtual Machine verantwortlich. Der Zweck der Übergabe des Klassenladeprogramms besteht darin, der Proxy.newProxyInstance-Methode mitzuteilen, wo sie nach der Klassendefinition suchen und wie die Proxy-Klasse in den Speicher geladen werden soll. Normalerweise verwenden wir den Klassenlader der Zielklasse, da wir möchten, dass die Proxy-Klasse in denselben Klassenlader wie die Zielklasse geladen wird. Dadurch wird sichergestellt, dass die Proxy-Klasse Zugriff auf die Schnittstelle der Zielklasse hat. In den meisten Fällen kann der Klassenlader der Zielklasse über target.getClass().getClassLoader() abgerufen werden.
Interface-Array (Interface[]): Das Interface-Array definiert die Schnittstelle, die die Proxy-Klasse implementieren muss. Diese Schnittstellen sind normalerweise dieselben wie die von der Zielklasse implementierten, sodass die Proxy-Klasse die Zielklasse transparent ersetzen kann. Wenn die Methoden des Proxy-Objekts aufgerufen werden, verarbeitet der InvocationHandler die in diesen Schnittstellen definierten Methoden. In den meisten Fällen können die von der Zielklasse implementierten Schnittstellen über target.getClass().getInterfaces() abgerufen werden.
InvocationHandler-Instanz: Dieser Parameter ist eine Instanz der InvocationHandler-Implementierung, die für die Verarbeitung von Methodenaufrufen von Proxy-Objekten verantwortlich ist. Wenn die Methode des Proxy-Objekts aufgerufen wird, wird die invoke()-Methode des InvocationHandler ausgeführt. Wir können das Verhalten des Proxy-Objekts anpassen, indem wir eine InvocationHandler-Instanz an die Proxy.newProxyInstance-Methode übergeben.
Obwohl die Proxy.newProxyInstance-Methode drei Parameter erfordert, dienen sie alle dazu, sicherzustellen, dass die Proxy-Klasse korrekt geladen werden kann, die Schnittstelle der Zielklasse zu implementieren und das Verhalten des Proxy-Objekts anzupassen. Hoffentlich hilft Ihnen diese Erklärung zu verstehen, was diese Parameter bewirken. Wenn Sie weitere Fragen haben, können Sie diese gerne stellen.
[Liu Der Grund, warum ich es im Detail erkläre, besteht darin, sicherzustellen, dass Sie die Parameter dieser Methode und ihre Funktionsweise vollständig verstehen. Wenn Sie unterwegs auf weitere Fragen stoßen oder eine prägnantere Erklärung benötigen, können Sie diese gerne stellen. Ich werde mein Bestes geben, um Ihnen zu helfen. Ich wünsche dir viel Erfolg beim Studium!
Highlights
1 Es beginnt mit einem einfachen Konzept (statischer Proxy), was sehr gut ist!
2. Wenn ich ein Konzept nicht verstehe, erklärt er es anhand von Beispielen, was sehr gut ist!
3. Wenn ich bewusst mein Unverständnis für das Gesagte zum Ausdruck brachte, konnte es jedes Mal eine weitere Erklärung liefern.
4. Es ist ein bisschen eifrig, dynamische Agenten vorzustellen, und ich kann bei meinen wiederholten Fragen auf die Grundkonzepte zurückgreifen.
Technisch erklärt es genau:
1 Warum es einen dynamischen Proxy gibt und den Unterschied zwischen ihm und dem dynamischen Proxy.
2. Warum muss Proxy mit der ursprünglichen Klassenschnittstelle identisch sein?
3. Warum das Erstellen eines dynamischen Proxys drei Parameter erfordert.
4. Einführung einiger Designprinzipien (einzelne Verantwortung), Entkopplung, Wiederverwendung und anderer Funktionen.
Ob es sich um die Lehrmethode oder die Wissensvermittlung handelt, GPT-4 hat meiner Meinung nach sehr gute Leistungen erbracht, selbst wenn es einen hervorragenden Dozenten gibt, ist dies wahrscheinlich der Fall.
Es fühlt sich einfach etwas ausführlich an. Es wäre besser, wenn es Spracheffekte unterstützen würde.
Egal wie GPT-4 intern umgesetzt wird, es verhält sich wie ein Programmiermeister. Es beherrscht nicht nur das Konzept des dynamischen Proxys, es verfügt auch über eine große Anzahl von Beispielen, die es unterstützen, und das kann es auch Ich habe es auch vom Einfachen zum Tiefgründigen erklärt. Es ist sehr leistungsfähig. Ich werde seine Designfähigkeiten später ausprobieren und mit Ihnen teilen.
Das obige ist der detaillierte Inhalt vonGPT-4 ist ein Meister der Programmierung, davon bin ich wirklich überzeugt!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!