ホームページ  >  記事  >  テクノロジー周辺機器  >  GPT-4 はプログラミングの達人だ、と私は心から確信しています。

GPT-4 はプログラミングの達人だ、と私は心から確信しています。

王林
王林転載
2023-04-29 17:28:072226ブラウズ

先週、数学を教える GPT-4 の紹介をお送りしましたが、多くの人がショックを受け、これが教育業界に革命を起こすかもしれないと感じました。

プログラミング分野ではどのように機能しますか?プログラマーの代わりになるかどうかについては話さないようにしましょう。これにはまだテストとフィードバックが必要です。まずは数学のようにプログラミングを教えられるかどうかを試してみたいと思います。

Java プログラミングでは理解するのが難しい概念である動的プロキシを見つけてテストしました。

以下は、私が Java プログラミングを学習している初心者のふりをして、プロキシ パターンを知らないふりをして、Java がどのように教えてくれたかを確認した会話の全文です。

内容は少し長いので、気長に読んでAIの実力を感じてみることをお勧めします。

どうしても我慢できない場合は、最後までスキップしてハイライトを直接ご覧ください。

[Liu Xin]: あなたはいつもソクラテス風に質問に答える家庭教師だとしましょう。直接答えは教えてくれませんが、考えるように導いてくれるでしょう?

[GPT]: もちろんです。ご質問がございましたら、考えて話し合うようご案内させていただきます。

[Liu Xin]: Java を学んでいますが、動的プロキシという概念がありますが、理解できません

[GPT]: とてもわかりました。一緒にソクラテス メソッドを使用しましょうJava 動的プロキシを調べてください。まず、プロキシ パターンを理解する必要があります。代理店モデルについて何を知っていますか?プロキシパターンの目的は何ですか?

[Liu Xin]: ある人が別の人のために何かをするということですか?

[GPT]: はい、ある意味、プロキシ パターンはソフトウェア設計に同様の概念を実装することです。この場合、あるオブジェクト (エージェント) が別のオブジェクト (ターゲット オブジェクト) に代わって特定のタスクを実行します。さて、考えてみてください。なぜプログラミングでプロキシ パターンを使用する必要があるのでしょうか?プロキシ モードの使用はどのような状況で役立ちますか?

[Liu Xin]: わかりません

[GPT]: それは問題ではありません。考えられる理由をいくつか一緒に調べてみましょう。プロキシ パターンは、オブジェクトにアクセスする前または後に追加の操作を実行する必要がある場合など、多くのシナリオで役立ちます。たとえば、次のような状況でプロキシ パターンを使用できます。

ターゲット オブジェクトへのアクセスを制御する (アクセス制御)

ターゲット オブジェクトが操作を実行する前後に追加の機能を追加する (たとえば、ロギング、キャッシュなど)

遅延読み込み、本当に必要な場合にのみターゲット オブジェクトを作成する

これらは、プロキシ パターンのアプリケーション シナリオの一部にすぎません。では、これらのシナリオでは動的プロキシがどのように作用すると思いますか?動的プロキシと静的プロキシの違いは何ですか?

[Liu Xin] これはどういう意味ですか。理解できません。

[GPT] それは問題ではありません。簡単な例を通してプロキシ モードを理解するのに役立ちます。

データを取得するためにリモート サーバーに接続する必要があるアプリケーションがあるとします。この場合、リモート サーバーとの接続を確立し、データを取得する役割を担う RemoteServerConnection というクラスを作成できます。

ここで、アプリケーションは、リモート サーバーに接続する前のネットワーク ステータスのチェックや、データのフェッチ後のログ記録など、追加のアクションを実行する必要があります。現時点では、プロキシ モードを使用できます。

RemoteServerConnectionProxy というクラスを作成できます。このクラスは RemoteServerConnection と同じインターフェイスを持ちますが、ネットワーク ステータスのチェックとログ記録の機能が追加されています。アプリケーションは、RemoteServerConnection を直接使用するのではなく、RemoteServerConnectionProxy を使用してデータを取得します。 RemoteServerConnectionProxy は追加の操作を処理し、必要に応じてアプリケーションに代わって RemoteServerConnection と通信します。

この例では、RemoteServerConnectionProxy は、特定のタスクを実行する RemoteServerConnection (ターゲット オブジェクト) を表すプロキシです。

プロキシ モデルの基本を理解したところで、動的プロキシを見てみましょう。上記の例では、別のプロキシ クラス (静的プロキシ) を作成しました。静的プロキシとは異なり、動的プロキシは、事前に作成されるのではなく、実行時に動的に生成されます。動的エージェントは、さまざまなシナリオにより柔軟に対応できます。

Java では、動的プロキシは通常、InvocationHandler インターフェイスを実装し、Proxy クラスを使用することによって実装されます。では、状況によっては、なぜ動的プロキシが静的プロキシよりも利点があるのか​​を考えてみましょう。

[Liu Xin]: ちょっと待って、まず静的プロキシについての話を終わらせましょう。あなたの例では

RemoteServerConnectionProxy は、ネットワークの状態を確認したり、以前のクラスにログを記録したりするための機能をいくつか追加していますが、なぜ RemoteServerConnection と同じインターフェイスを持っているのでしょうか?

[GPT]: 申し訳ありませんが、前に明確に説明できませんでした。プロキシ パターンの目的を確認してみましょう。1 つのオブジェクト (プロキシ) が、別のオブジェクト (ターゲット オブジェクト) に代わって特定のタスクを実行します。プロキシ オブジェクトがターゲット オブジェクトに代わってタスクを正常に実行するには、それらのオブジェクトが同じインターフェイスを持っている必要があります。このようにすると、クライアント コードは、プロキシ オブジェクトとターゲット オブジェクトのどちらと対話しているかを気にしません。これは、それらが同じインターフェイスを持っているためです。

この例では、RemoteServerConnectionProxy (プロキシ オブジェクト) と RemoteServerConnection (ターゲット オブジェクト) は同じインターフェイスを持ちます。これは、アプリケーションがこれら 2 つのクラスを透過的に使用できるためです。アプリケーションでプロキシを使用することに決めた場合、プロキシ オブジェクトとターゲット オブジェクトの両方が同じインターフェイスを実装しているため、クライアント コードを変更する必要はありません。これが、プロキシ オブジェクトとターゲット オブジェクトが同じインターフェイスを持つ必要がある理由です。

さらに、同じインターフェイスを持つということは、タスクを実行するときにプロキシ オブジェクトがターゲット オブジェクトのメソッドを呼び出すことができることを意味します。プロキシ オブジェクトは適切なタイミングでターゲット オブジェクトに制御を渡す必要があるため、これはアクセス制御、ログ記録などの追加機能を実装する場合に重要です。

[Liu Xin]: わかりました、これはプロキシ モードです。では、動的プロキシとは何ですか?

[GPT]: とてもわかりました。プロキシ モードについては理解できたので、引き続き動的プロキシについて説明しましょう。

動的プロキシと静的プロキシの主な違いは、その作成方法です。静的プロキシは事前に作成されたプロキシ クラスです。つまり、ターゲット クラスごとに個別のプロキシ クラスを作成する必要があります。ただし、動的プロキシは実行時に動的に生成されるため、ソース コードを変更せずに複数のクラスの汎用プロキシを作成できます。こうすることで、コードの重複が減り、保守性が向上します。

Java では、動的プロキシは主に java.lang.reflect.Proxy クラスと java.lang.reflect.InvocationHandler インターフェイスを通じて実装されます。動的プロキシの基本的な手順は次のとおりです。

プロキシ オブジェクトのメソッド呼び出しを処理する InvocationHandler 実装クラスを定義します。このクラスでは、必要に応じて、ロギング、アクセス制御などの追加の操作を追加できます。

Proxy.newProxyInstance() メソッドを使用して動的プロキシを作成します。このメソッドは、クラス ローダー、インターフェイス配列 (プロキシ クラスが実装する必要があるインターフェイス)、および InvocationHandler インスタンスの 3 つのパラメーターを受け取ります。このメソッドは、指定されたインターフェイスを実装するプロキシ オブジェクトを返します。

クライアント コードでは、作成された動的プロキシ オブジェクトを使用してメソッドを呼び出します。プロキシ オブジェクトのメソッドを呼び出すと、その呼び出しが InvocationHandler インスタンスの invoke() メソッドに転送されます。

これにより、クラスごとに個別のプロキシ クラスを作成しなくても、実行時に 1 つ以上のクラスの動的プロキシを作成できます。

動的プロキシの概念を理解したところで、静的プロキシと比較した動的プロキシの利点と制限について考えてください。

[Liu Xin] は混乱しているようでした

##[GPT] それは問題ではありません。簡単な例を通して動的プロキシを理解できるようにしましょう。

銀行インターフェイスがあり、転送操作を実行するメソッド transfer() があるとします。このインターフェイスにログ機能を追加したいと考えていますが、Bank インターフェイスの実装は変更したくありません。現時点では、動的プロキシを使用できます。

まず、InvocationHandler を実装する必要があります。このクラスは、プロキシ オブジェクトのメソッド呼び出しの処理と、ログ機能の追加を担当します。

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;
}
}

次に、 newProxyInstance() メソッドは、Bank インターフェイスの動的プロキシを作成します。 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
);

これで、proxyBank を使用して transfer() メソッドを呼び出すと、ロギング関数が自動的に追加されます:

proxyBank.transfer();

Pass この例では、動的プロキシの主な利点がわかります。クラスごとに個別のプロキシ クラスを記述する代わりに、実行時に 1 つ以上のクラスに対して汎用プロキシを作成できます。同時に、コードがより柔軟になり、保守が容易になります。

ただし、動的プロキシにはいくつかの制限もあります。たとえば、Java では、動的プロキシはインターフェイスを実装するクラスのプロキシのみを作成できます。クラスがインターフェイスを実装していない場合、動的プロキシは使用できません。

この例が動的プロキシの概念を理解するのに役立つことを願っています。まだご不明な点がございましたら、お気軽にお問い合わせください。

[Liu Xin]: あなたの例では、Bank インターフェイスにログ機能を追加したいと考えています。その場合は、BankProxy を作成してログ コードを追加するだけでよいでしょうか?なぜわざわざInvocationHandlerを使うのでしょうか?

[GPT]: とても良い質問をしましたね。場合によっては、静的プロキシ (先ほど言及した BankProxy など) を使用すると、必要な機能を実際に実現できることがあります。ただし、より複雑なシナリオでは、動的プロキシがいくつかの重要な利点をもたらす可能性があります。

汎用性: 動的プロキシは、個別のエージェント クラスを作成することなく、複数のクラスおよびインターフェイスのプロキシを作成できます。ロギング、アクセス制御など、同じ機能を多くのインターフェイスに追加する必要がある場合、動的プロキシは重複コードを減らし、コードの再利用性を向上させるのに役立ちます。

メンテナンスが簡単: 将来プロキシの機能を変更または追加する必要がある場合は、動的プロキシを使用する方が簡単です。静的プロキシの場合は、各プロキシ クラスを 1 つずつ変更する必要がありますが、動的プロキシの場合は、InvocationHandler 実装を変更するだけで済みます。このようにして、元のクラスやインターフェイスを変更せずに、エージェントの動作を簡単に変更できます。

分離: 動的プロキシは、プロキシ ロジックをビジネス ロジックから分離するのに役立ちます。プロキシ ロジックを変更する必要がある場合、元のクラスやインターフェイスを変更する必要はありません。これにより、コードの理解と保守が容易になります。

それでも、動的プロキシが常に最適なソリューションであるとは限りません。場合によっては、静的プロキシの方がニーズに適している場合があります。動的プロキシと静的プロキシのどちらを使用するかを選択する場合は、特定のシナリオとニーズに基づいて長所と短所を比較検討する必要があります。

この回答が、動的プロキシと静的プロキシの違いをより深く理解するのに役立つことを願っています。他にご質問がございましたら、お気軽にお問い合わせください。

[Liu Xin]: 分かりましたが、なぜ Proxy.newProxyInstance を通じて新しいクラスを作成する必要があるのでしょうか?元の RealBank クラスを直接変更するのは良いことではありませんか?

[GPT]: あなたの質問には、コード設計と分離の概念が含まれています。もちろん、RealBank クラスを直接変更してロギング機能を追加することは可能ですが、これにより次の問題が発生する可能性があります。

単一責任の原則: 単一責任の原則によれば、クラスは 1 つの責任のみを担当する必要があります。クリア機能。 RealBank クラスの主な役割は、Bank インターフェイスを実装し、銀行関連のビジネス ロジックを処理することです。 RealBank クラスにロギング機能を追加すると、RealBank クラスがビジネス ロジックとロギングの両方を担当することになり、単一責任の原則に違反する可能性があります。プロキシを使用すると、これら 2 つの役割を分離して、各クラスが明確に定義された機能に集中できるようになります。

分離: ロギング機能を RealBank クラスに直接追加すると、将来ロギング機能を変更または削除する必要があるときに、RealBank クラスの実装を変更する必要があります。これにより、RealBank クラスがロギング機能と密接に結合され、コードの保守性が低下する可能性があります。プロキシを使用すると、ロギング機能を RealBank クラスから分離できるため、ロギング機能を変更または削除する必要がある場合に、RealBank クラスを変更する必要がなくなります。

再利用性: 複数のクラスにログ機能を追加したいとします。各クラスの実装を直接変更すると、コードが大量に重複することになります。プロキシ (特に動的プロキシ) を使用すると、複数のクラスに共通のロギング機能を提供できるため、コードの重複が減り、コードの再利用性が向上します。

つまり、プロキシを使用すると、単一責任の原則に従い、分離を実現し、コードの再利用性を向上させることができます。元のクラスを直接変更することが可能な場合もありますが、プロキシを使用することでコードの設計と保守性が向上するシナリオも数多くあります。

この回答がお役に立てば幸いです。他にご質問がございましたら、お気軽にお問い合わせください。

[Liu Xin]: Proxy.newProxyInstance のパラメータは非常に複雑です。

InvocationHandler を渡すことは理解しました。なぜ他の 2 つのパラメータを渡す必要があるのでしょうか?

[GPT]: Proxy.newProxyInstance メソッドには 3 つのパラメーターが必要です。その機能について詳しく説明します:

ClassLoader: クラス ローダーは、バイト コードを Java 仮想マシンにロードする役割を果たします。クラス ローダーを渡す目的は、クラス定義を探す場所とプロキシ クラスをメモリにロードする方法を Proxy.newProxyInstance メソッドに知らせることです。通常、プロキシ クラスをターゲット クラスと同じクラス ローダーにロードする必要があるため、ターゲット クラスのクラス ローダーを使用します。これにより、プロキシ クラスがターゲット クラスのインターフェイスにアクセスできるようになります。ほとんどの場合、ターゲット クラスのクラス ローダーは、target.getClass().getClassLoader() を通じて取得できます。

インターフェイス配列 (Interface[]): インターフェイス配列は、プロキシ クラスが実装する必要があるインターフェイスを定義します。これらのインターフェイスは通常、ターゲット クラスによって実装されるインターフェイスと同じであるため、プロキシ クラスはターゲット クラスを透過的に置き換えることができます。プロキシ オブジェクトのメソッドが呼び出されると、InvocationHandler はこれらのインターフェイスで定義されたメソッドを処理します。ほとんどの場合、ターゲット クラスによって実装されたインターフェイスは、target.getClass().getInterfaces() を通じて取得できます。

InvocationHandler インスタンス: このパラメーターは、プロキシ オブジェクトのメソッド呼び出しの処理を担当する InvocationHandler 実装のインスタンスです。プロキシ オブジェクトのメソッドが呼び出されると、InvocationHandler の invoke() メソッドが実行されます。 InvocationHandler インスタンスを Proxy.newProxyInstance メソッドに渡すことで、プロキシ オブジェクトの動作をカスタマイズできます。

Proxy.newProxyInstance メソッドには 3 つのパラメーターが必要ですが、これらはすべて、プロキシ クラスが正しくロードされ、ターゲット クラスのインターフェイスを実装し、プロキシ オブジェクトの動作をカスタマイズできることを確認するためのものです。この説明がこれらのパラメーターの機能を理解するのに役立つことを願っています。他にご質問がございましたら、お気軽にお問い合わせください。

[Liu Xin]: あなたの答えは少し長いですが、それでも理解できます。

[GPT]: 理解していただけてうれしいです。詳細に説明する理由は、このメソッドのパラメータとその動作を完全に理解していただくためです。学習中に他の質問に遭遇した場合、またはより簡潔な説明が必要な場合は、お気軽に質問してください。全力でお手伝いさせていただきます。あなたの勉強がうまくいくことを祈っています!

ハイライト

教育方法に関して:

1. シンプルな概念 (静的プロキシ) から始まり、非常に優れています。

2. 概念が理解できないときは、例を挙げて説明してくれるのがとても良いです。

3. 書かれていることについて意図的に理解できないことを表現すると、そのたびにさらに詳しく説明してもらえます。

4. 動的エージェントの導入には少し熱心ですが、繰り返し質問しているうちに基本概念に戻ることがあります。

技術的には、正確に説明されています:

1. 動的プロキシが存在する理由と、動的プロキシとの違い。

2. プロキシが元のクラス インターフェイスと同じである必要があるのはなぜですか?

3. 動的プロキシの作成に 3 つのパラメータが必要な理由。

4. いくつかの設計原則 (単一責任)、分離、再利用、その他の機能を導入しました。

GPT-4は教え方にしても知識の教え方にしても非常に優れており、たとえ優秀な講師がいたとしてもこれは当てはまるのではないかと思います。

ちょっと冗長な気がするので、音声エフェクトに対応してくれるとさらに良いです。

GPT-4 が内部でどのように行われていても、外部ではプログラミング マスターのように動作します。動的プロキシの概念を習得しているだけでなく、それをサポートするサンプルが多数あり、また、シンプルなものから奥深いものまで説明します。非常に強力です。後でそのデザイン機能を試して共有します。

以上がGPT-4 はプログラミングの達人だ、と私は心から確信しています。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事は51cto.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。