La semaine dernière, je vous ai envoyé une introduction à GPT-4 pour l'enseignement des mathématiques. De nombreuses personnes ont été choquées et ont estimé que cela pourrait déclencher une révolution dans le secteur de l'éducation.
Comment se comporte-t-il dans le domaine de la programmation ? Ne parlons pas de savoir s'il peut remplacer les programmeurs. Cela nécessite encore plus de tests et de commentaires. Je veux d'abord essayer de savoir s'il peut enseigner la programmation comme les mathématiques.
J'ai trouvé un concept difficile à comprendre en programmation Java : le proxy dynamique et je l'ai testé.
Vous trouverez ci-dessous la conversation complète que j'ai eue avec lui, au cours de laquelle j'ai fait semblant d'être un débutant apprenant la programmation Java et je ne connaissais pas le modèle de proxy pour voir comment il m'a appris.
Le contenu est un peu long, il est recommandé de le lire patiemment et de ressentir les capacités de l'IA.
Si vous ne pouvez vraiment pas le regarder, vous pouvez passer à la fin et regarder directement les moments forts.
【Liu Xin】 : Supposons que vous soyez un tuteur qui répond toujours aux questions dans un style socratique. Vous ne me donnerez pas la réponse directement, mais vous me guiderez dans la réflexion, d'accord ?
【GPT】 : Bien sûr. Si vous avez des questions, laissez-moi vous guider pour réfléchir et discuter.
[Liu Xin] : J'apprends Java, et il existe un concept de proxy dynamique, que je n'arrive pas à comprendre
[GPT] : Très bien, utilisons la méthode socratique pour discuter du proxy dynamique Java. Tout d’abord, nous devons comprendre le modèle de proxy. Que savez-vous du modèle d’agence ? Quel est le but du modèle de proxy ?
【Liu Xin】:Est-ce que cela signifie qu'une personne prend la place d'une autre personne ?
【GPT】 : Oui, dans un sens, le modèle de proxy consiste à implémenter des concepts similaires dans la conception de logiciels. Dans ce cas, un objet (l'agent) effectue une tâche spécifique pour le compte d'un autre objet (l'objet cible). Maintenant, réfléchissez-y, pourquoi pourrions-nous avoir besoin d'utiliser le modèle proxy dans la programmation ? Dans quelles situations l’utilisation du mode proxy serait-elle utile ?
[Liu Xin] : Je ne sais pas
[GPT] : Cela n'a pas d'importance, explorons ensemble quelques raisons possibles. Le modèle de proxy est utile dans de nombreux scénarios, par exemple lorsque nous devons effectuer des opérations supplémentaires avant ou après avoir accédé à un objet. Par exemple, nous pouvons utiliser le modèle proxy dans les situations suivantes :
Contrôler l'accès à l'objet cible (contrôle d'accès)
Ajouter des fonctionnalités supplémentaires (telles que la journalisation, la mise en cache, etc.) avant et après que l'objet cible effectue des opérations
Chargement paresseux, seulement après Créez l'objet cible uniquement lorsqu'il est vraiment nécessaire
Ce ne sont là que quelques scénarios d'application du modèle de proxy. Alors, à votre avis, comment les proxys dynamiques entrent-ils en jeu dans ces scénarios ? Quelle est la différence entre les proxys dynamiques et les proxys statiques ?
[Liu Xin] Qu'est-ce que cela signifie ? Je ne comprends pas.
[GPT] Cela n'a pas d'importance. Nous pouvons vous aider à comprendre le mode proxy à travers un exemple simple.
Supposons que vous ayez une application qui doit se connecter à un serveur distant pour obtenir des données. Dans ce cas, vous pouvez créer une classe appelée RemoteServerConnection, chargée d'établir une connexion avec le serveur distant et d'obtenir des données.
Maintenant, votre application doit effectuer certaines actions supplémentaires, telles que vérifier l'état du réseau avant de se connecter au serveur distant et se connecter après avoir récupéré les données. À ce stade, vous pouvez utiliser le mode proxy.
Vous pouvez créer une classe appelée RemoteServerConnectionProxy, qui a la même interface que RemoteServerConnection mais ajoute la fonctionnalité de vérification de l'état du réseau et de journalisation. L'application utilisera RemoteServerConnectionProxy pour obtenir des données au lieu d'utiliser directement RemoteServerConnection. Le RemoteServerConnectionProxy gérera des opérations supplémentaires, puis communiquera avec RemoteServerConnection au nom de l'application en cas de besoin.
Dans cet exemple, RemoteServerConnectionProxy est un proxy qui représente RemoteServerConnection (objet cible) pour effectuer des tâches spécifiques.
Maintenant que vous avez une compréhension de base du modèle de proxy, examinons le proxy dynamique. Dans l'exemple ci-dessus, nous avons créé une classe proxy distincte (proxy statique). Contrairement aux proxys statiques, les proxys dynamiques sont générés dynamiquement au moment de l'exécution plutôt qu'écrits à l'avance. Les agents dynamiques peuvent répondre à différents scénarios de manière plus flexible.
En Java, le proxy dynamique est généralement implémenté en implémentant l'interface InvocationHandler et en utilisant la classe Proxy. Alors réfléchissez-y, pourquoi les proxys dynamiques pourraient avoir des avantages par rapport aux proxys statiques dans certaines situations ?
[Liu Xin] : Attendez une minute, finissons d'abord de parler du proxy statique. Dans votre exemple
.RemoteServerConnectionProxy ajoute quelques fonctions pour vérifier l'état du réseau et enregistrer les journaux à la classe précédente, mais pourquoi a-t-il la même interface que RemoteServerConnection ?
【GPT】 : Désolé, je ne l'ai pas expliqué clairement auparavant. Passons en revue l'objectif du modèle de proxy : un objet (le proxy) effectue une tâche spécifique pour le compte d'un autre objet (l'objet cible). Pour que l'objet proxy puisse effectuer avec succès des tâches au nom de l'objet cible, ils doivent avoir la même interface. De cette façon, le code client ne se soucie pas de savoir s'il interagit avec l'objet proxy ou avec l'objet cible, car ils ont la même interface.
Dans notre cas, RemoteServerConnectionProxy (objet proxy) et RemoteServerConnection (objet cible) ont la même interface car de cette façon l'application peut utiliser ces deux classes de manière transparente. Lorsque nous décidons d'utiliser un proxy dans notre application, nous n'avons pas besoin de modifier le code client car l'objet proxy et l'objet cible implémentent la même interface. C'est pourquoi l'objet proxy et l'objet cible doivent avoir la même interface.
De plus, avoir la même interface signifie que l'objet proxy peut appeler les méthodes de l'objet cible lors de l'exécution de tâches. Ceci est important pour implémenter des fonctionnalités supplémentaires telles que le contrôle d'accès, la journalisation, etc., car l'objet proxy doit transmettre le contrôle à l'objet cible au moment approprié.
[Liu Xin] : Compris, c'est le modèle proxy. Alors, qu'est-ce qu'un proxy dynamique ?
【GPT】 : Super, maintenant que vous comprenez le mode proxy, passons au proxy dynamique.
La principale différence entre les proxys dynamiques et les proxys statiques réside dans la manière dont ils sont créés. Les proxys statiques sont des classes proxy pré-écrites, ce qui signifie que pour chaque classe cible, vous devez créer une classe proxy distincte pour celle-ci. Cependant, les proxys dynamiques sont générés dynamiquement au moment de l'exécution, ce qui vous permet de créer des proxys génériques pour plusieurs classes sans modifier le code source. De cette façon, vous réduisez la duplication de code et améliorez la maintenabilité.
En Java, le proxy dynamique est principalement implémenté via la classe java.lang.reflect.Proxy et l'interface java.lang.reflect.InvocationHandler. Voici les étapes de base du proxy dynamique :
Définissez une classe d'implémentation InvocationHandler, qui est responsable de la gestion des appels de méthode sur l'objet proxy. Dans cette classe, vous pouvez ajouter des opérations supplémentaires selon vos besoins, telles que la journalisation, le contrôle d'accès, etc.
Utilisez la méthode Proxy.newProxyInstance() pour créer un proxy dynamique. Cette méthode reçoit trois paramètres : le chargeur de classe, le tableau d'interface (l'interface que la classe proxy doit implémenter) et l'instance InvocationHandler. Cette méthode renvoie un objet proxy qui implémente l'interface spécifiée.
Dans le code client, utilisez l'objet proxy dynamique créé pour appeler des méthodes. Lorsque vous appelez une méthode sur l'objet proxy, celle-ci transmet l'appel à la méthode Invocation() de l'instance InvocationHandler.
De cette façon, vous pouvez créer des proxys dynamiques pour une ou plusieurs classes au moment de l'exécution sans avoir à écrire une classe proxy distincte pour chaque classe.
Maintenant que vous comprenez le concept des proxys dynamiques, réfléchissez aux avantages et aux limites des proxys dynamiques par rapport aux proxys statiques.
[Liu Xin] avait l'air confus
[GPT] Ce n'est pas grave, essayons de vous aider à comprendre le proxy dynamique à travers un exemple simple.
Supposons que nous ayons une interface bancaire, qui dispose d'une méthode transfer() pour effectuer les opérations de transfert. Nous souhaitons ajouter une fonctionnalité de journalisation à cette interface, mais nous ne souhaitons pas modifier l'implémentation de l'interface Banque. Pour le moment, nous pouvons utiliser un proxy dynamique.
Tout d'abord, nous devons implémenter un InvocationHandler. Cette classe sera responsable de la gestion des appels de méthode sur l'objet proxy et de l'ajout de fonctionnalités de journalisation :
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; } }
Maintenant, nous pouvons utiliser la méthode Proxy.newProxyInstance() pour créer une dynamique. proxy pour l'interface Banque. Notez que nous devons transmettre l'instance de classe d'implémentation de l'interface Bank à LoggingHandler :
Bank realBank = new RealBank(); // 假设RealBank是Bank接口的一个实现 InvocationHandler handler = new LoggingHandler(realBank); Bank proxyBank = (Bank) Proxy.newProxyInstance( realBank.getClass().getClassLoader(), realBank.getClass().getInterfaces(), handler );
Désormais, lorsque nous utilisons proxyBank pour appeler la méthode transfer(), la fonction de journalisation sera automatiquement ajoutée :
proxyBank.transfer();
Avec cet exemple, nous peut voir la dynamique Le principal avantage des proxys : au lieu d'écrire une classe proxy distincte pour chaque classe, un proxy générique peut être créé au moment de l'exécution pour une ou plusieurs classes. Dans le même temps, cela rend également le code plus flexible et plus facile à maintenir.
Cependant, les proxys dynamiques ont également certaines limites. Par exemple, en Java, les proxys dynamiques ne peuvent créer des proxys que pour les classes qui implémentent des interfaces. Si la classe n'implémente aucune interface, les proxys dynamiques ne peuvent pas être utilisés.
J'espère que cet exemple vous aidera à comprendre le concept de proxy dynamique. Si vous avez encore des questions, n'hésitez pas à les poser.
[Liu Xin] : Dans votre exemple, nous espérons ajouter une fonction de journalisation à l'interface de la banque. Ensuite, je peux simplement écrire un BankProxy et ajouter le code de journalisation ? Pourquoi se donner la peine d'utiliser InvocationHandler ?
[GPT] : Vous avez posé une très bonne question. Dans certains cas, l'utilisation d'un proxy statique (comme le BankProxy que vous avez mentionné) peut effectivement obtenir la fonctionnalité souhaitée. Cependant, dans des scénarios plus complexes, les proxys dynamiques peuvent apporter des avantages significatifs :
Polyvalence : les proxys dynamiques peuvent créer des proxys pour plusieurs classes et interfaces sans avoir besoin d'écrire des classes proxy distinctes pour chaque classe. Si vous devez ajouter la même fonctionnalité à de nombreuses interfaces, telles que la journalisation, le contrôle d'accès, etc., les proxys dynamiques peuvent vous aider à réduire le code en double et à améliorer la réutilisabilité du code.
Facile à entretenir : si vous devez modifier ou ajouter des fonctionnalités à votre proxy à l'avenir, il est plus facile d'utiliser un proxy dynamique. Pour les proxys statiques, vous devez modifier chaque classe de proxy une par une ; pour les proxys dynamiques, il vous suffit de modifier l'implémentation d'InvocationHandler. De cette façon, vous pouvez facilement modifier le comportement de l'agent sans modifier les classes et interfaces d'origine.
Découplage : les proxys dynamiques aident à séparer la logique proxy de la logique métier. Lorsque vous devez modifier la logique du proxy, il n'est pas nécessaire de modifier la classe et l'interface d'origine. Cela rend le code plus facile à comprendre et à maintenir.
Pourtant, les proxys dynamiques ne sont pas toujours la meilleure solution. Dans certains cas, un proxy statique peut mieux répondre à vos besoins. Lorsque vous choisissez entre l'utilisation d'un proxy dynamique ou d'un proxy statique, vous devez peser le pour et le contre en fonction de scénarios et de besoins spécifiques.
J'espère que cette réponse pourra vous aider à mieux comprendre la différence entre un proxy dynamique et un proxy statique. Si vous avez d'autres questions, n'hésitez pas à les poser.
[Liu Xin] : Compris, mais pourquoi devons-nous créer une nouvelle classe via Proxy.newProxyInstance ? N'est-il pas bon de modifier directement la classe RealBank d'origine ? [GPT] : Votre question concerne les concepts de conception de code et de découplage. Il est certainement possible de modifier directement la classe RealBank pour ajouter une fonctionnalité de journalisation, mais cela peut entraîner les problèmes suivants :
Principe de responsabilité unique : selon le principe de responsabilité unique, une classe ne doit être responsable que d'une seule fonction claire. La principale responsabilité de la classe RealBank est d'implémenter l'interface bancaire et de gérer la logique métier liée à la banque. Si nous ajoutons une fonctionnalité de journalisation dans la classe RealBank, elle sera responsable à la fois de la logique métier et de la journalisation, ce qui peut violer le principe de responsabilité unique. En utilisant des proxys, nous pouvons séparer ces deux responsabilités afin que chaque classe se concentre sur une fonction claire.
Découplage : si nous ajoutons la fonctionnalité de journalisation directement à la classe RealBank, nous devrons modifier l'implémentation de la classe RealBank si nous devons modifier ou supprimer la fonctionnalité de journalisation à l'avenir. Cela peut avoir pour conséquence que la classe RealBank soit étroitement couplée à la fonctionnalité de journalisation, réduisant ainsi la maintenabilité du code. En utilisant un proxy, nous pouvons séparer la fonctionnalité de journalisation de la classe RealBank de sorte que si nous devons modifier ou supprimer la fonctionnalité de journalisation, nous n'avons pas besoin de changer la classe RealBank.
Réutilisabilité : supposons que nous souhaitions ajouter une fonctionnalité de journalisation à plusieurs classes. Si l’on modifie directement l’implémentation de chaque classe, cela entraînera de nombreuses duplications de code. En utilisant des proxys (en particulier des proxys dynamiques), nous pouvons fournir des fonctions de journalisation communes pour plusieurs classes, réduisant ainsi la duplication de code et améliorant la réutilisabilité du code.
En bref, en utilisant des agents, nous pouvons suivre le principe de responsabilité unique, réaliser le découplage et améliorer la réutilisabilité du code. Bien que la modification directe de la classe d'origine soit réalisable dans certains cas, il existe de nombreux scénarios dans lesquels l'utilisation d'un proxy peut conduire à une meilleure conception et maintenabilité du code.
J'espère que cette réponse vous sera utile. Si vous avez d'autres questions, n'hésitez pas à les poser.
[Liu Xin] : Les paramètres de Proxy.newProxyInstance sont si compliqués. Je comprends que transmettre le
InvocationHandler. Pourquoi devons-nous transmettre les deux autres paramètres ?
【GPT】 : La méthode Proxy.newProxyInstance nécessite trois paramètres, laissez-moi vous expliquer leur rôle en détail :
ClassLoader : Le chargeur de classe est responsable du chargement du byte code dans la machine virtuelle Java. Le but du passage du chargeur de classe est de permettre à la méthode Proxy.newProxyInstance de savoir où rechercher la définition de classe et comment charger la classe proxy en mémoire. Habituellement, nous utilisons le chargeur de classe de la classe cible car nous voulons que la classe proxy soit chargée dans le même chargeur de classe que la classe cible. Cela garantit que la classe proxy a accès à l'interface de la classe cible. Dans la plupart des cas, le chargeur de classe de la classe cible peut être obtenu via target.getClass().getClassLoader().
Interface array (Interface[]) : Le tableau d'interface définit l'interface que la classe proxy doit implémenter. Ces interfaces sont généralement les mêmes que celles implémentées par la classe cible afin que la classe proxy puisse se substituer de manière transparente à la classe cible. Lorsque les méthodes de l'objet proxy sont appelées, le InvocationHandler gérera les méthodes définies dans ces interfaces. Dans la plupart des cas, les interfaces implémentées par la classe cible peuvent être obtenues via target.getClass().getInterfaces().
Instance InvocationHandler : ce paramètre est une instance de l'implémentation d'InvocationHandler, qui est responsable du traitement des appels de méthode des objets proxy. Lorsque la méthode de l'objet proxy est appelée, la méthode InvocationHandler () sera exécutée. En passant une instance InvocationHandler à la méthode Proxy.newProxyInstance, nous pouvons personnaliser le comportement de l'objet proxy.
Bien que la méthode Proxy.newProxyInstance nécessite trois paramètres, ils visent tous à garantir que la classe proxy peut être chargée correctement, à implémenter l'interface de la classe cible et à personnaliser le comportement de l'objet proxy. Espérons que cette explication vous aide à comprendre ce que font ces paramètres. Si vous avez d'autres questions, n'hésitez pas à les poser.
[Liu Xin] : Même si votre réponse est un peu longue, je la comprends toujours
[GPT] : Je suis content que vous compreniez. La raison pour laquelle je l'explique en détail est de m'assurer que vous comprenez parfaitement les paramètres de cette méthode et ce qu'elle fait. Si vous rencontrez d'autres questions pendant vos études ou si vous avez besoin d'une explication plus concise, n'hésitez pas à les poser. Je ferai de mon mieux pour vous aider. Je te souhaite bonne chance dans tes études !
Au niveau des méthodes pédagogiques :
1 Cela part d'un concept simple (proxy statique), qui est très bien !
2. Quand je ne comprends pas un concept, il m'explique avec des exemples, ce qui est très bien !
3. Lorsque j'exprimais délibérément mon incompréhension de ce qu'il disait, il pouvait à chaque fois l'expliquer davantage.
4. C'est un peu pressé de présenter des agents dynamiques, et ça peut revenir sur les concepts de base lors de mes questions répétées.
Techniquement, cela explique précisément :
1.Pourquoi il existe un proxy dynamique et la différence entre celui-ci et le proxy dynamique.
2. Pourquoi Proxy doit-il être identique à l'interface de classe d'origine ?
3. Pourquoi la création d'un proxy dynamique nécessite trois paramètres.
4. Introduction de quelques principes de conception (responsabilité unique), de découplage, de réutilisation et d'autres fonctions.
Qu'il s'agisse de la méthode d'enseignement ou de l'enseignement des connaissances, GPT-4 a très bien fonctionné, je pense que même s'il y a un excellent conférencier, c'est probablement le cas.
J'ai juste l'impression que c'est un peu verbeux. Ce serait mieux s'il prend en charge les effets vocaux.
Peu importe la manière dont GPT-4 est réalisé en interne, il se comporte comme un maître en programmation. Non seulement il maîtrise le concept de proxy dynamique, mais il dispose d'un grand nombre d'exemples pour le prendre en charge, et il peut le faire. J'apprends aussi du simple au profond. Je suis allé l'expliquer, il est très puissant, j'essaierai ses capacités de conception plus tard et je le partagerai avec vous.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!