自己紹介
みなさん、こんにちは!ごえもんです。ご存知の方は、お久しぶりです。時間が経つのは早いもので、僕もアライドアーキテクツに入社してから、なんと丸10年が経ちました。現在、僕はVPoPという職務についており、SaaSビジネスを開発を通じて成長させていくミッションを持って働いています。具体的には、プロダクトの開発方針を定めたり、新機能や既存機能の開発において意思決定を行ったりしています。また、弊社のプロダクト「Letro」の開発チームの総合PMもつとめています。
ChatGPT
最近はChatGPTをはじめとする生成系AIの企業での活用が進んでいますね。弊社もChatGPTのAPIを叩くWebインタフェースを用意して自社サーバーで動かしており、業務のさまざまな領域で活用を進めています。僕自身も、もはや普段の業務でChatGPTがないと仕事ができないんじゃないかというレベルで、色々な箇所で使っています。
特に有用だと感じているのは文章校正によるコミュニケーション質の向上です。例えば、開発アイテムの機能説明を行う際のドキュメントを「非エンジニアにも伝わるように」校正することは、チームの中でも限られた人しかできませんでした。これまでは一生懸命説明をして他の人に習得させていたようなことが、ChatGPTの出現により”特定のプロンプトを用意してChatGPTを通してもらう”という画期的な代替手段が出てきたのは素晴らしいことです。ChatGPTさまさまです。
もう僕はChatGPTがないと仕事ができないので、今回はこのブログコンテンツもChatGPTに作ってもらうことにしました。決して書くのが大変だから、とかそういう後ろ向きな理由ではありません。ChatGPTの可能性についてより知見を深めるための実験です。大切なことなので二回言いますが、決して書くのが大変だから、とかそういう後ろ向きな理由ではないことを誓います。完全に前向きな試みです。
ChatGPTでプログラミングの学習をしてみる
今はAIとともにプログラミングをするのがもはや当たり前の時代になってきていますが、それでもAIに完全にプログラムを作成させる部分を代替するまでには至っていません。つまり、人間がプログラミングの基礎をしっかりと抑えることの重要性は現時点では変わっていないと考えています。そこで、今回はChatGPTにプログラミング講師になりきってもらい、プログラムを教えてもらうという試みをしてみました。僕が初学者のころにわからなかったあんなことやこんなことも、きっとChatGPTならどこまででも教えてくれるでしょう。
プロンプトの準備をする
ChatGPTの最初のやりとりとして「あなたはプロの〇〇です」のように相手に前提やロールを与えて、その後のコミュニケーション質をあげるプロンプトをよく見ます。なので僕もそれにならって考えて与えてみました。
「なのだぜ!」のぶっこみ方が相当ヤケクソ感漂う感じでいいですね。その昔、「学校へ行こう!」という番組に出てきた家庭教師の「だぜ」を彷彿とさせる「生徒に寄り添って会話のノリを合わせているけど絶妙に噛み合っていない」という雰囲気が醸成されています。チャッピー先生もノリノリのようですし、今回はこれでいくことにします。
講義を開始してもらう
僕がオブジェクト指向について初学者だった頃、もっとも難しかった概念のひとつに「ポリモーフィズム」があります。今回はこれをチャッピー先生に聞いてみることにします。そしてここからはキーワード検索に引っかからなくてアクセス数が稼げないし少しでも読みやすいように画像ではなく文字で起こしていきます。
ごえもん:
チャッピー先生!ぼくは今オブジェクト指向について勉強しているんですが、ポリモーフィズムの利点がよくわからないんです。PHPのサンプルコードを用いて教えてください!
チャッピー先生:
おっ、ポリモーフィズムを理解したいんだね!いい質問だ!ポリモーフィズムはオブジェクト指向プログラミングの大切な概念だぜ!
ポリモーフィズムは、異なるクラスのオブジェクトが共通のインターフェースや親クラスを持つことで、同じメソッドを呼び出すことができる性質のことなのだぜ!これによって、コードが柔軟で拡張性があり、可読性も向上するんだ。
では、PHPでのサンプルコードを見てみよう!例えば、Animal
という基本クラスがあり、そのサブクラスとしてDog
とCat
があるとするね。
12345678910111213141516171819202122232425262728293031323334 <?php// 基本クラス Animalclass Animal {public function makeSound() {echo "Some generic sound\n";}}// Animalクラスを継承したDogクラスclass Dog extends Animal {public function makeSound() {echo "Bark!\n";}}// Animalクラスを継承したCatクラスclass Cat extends Animal {public function makeSound() {echo "Meow!\n";}}// ポリモーフィズムを利用して異なるクラスのオブジェクトを同じように扱うfunction makeAnimalSound(Animal $animal) {$animal->makeSound();}$dog = new Dog();$cat = new Cat();// DogとCatのmakeSoundメソッドを呼び出すmakeAnimalSound($dog); // Outputs: Bark!makeAnimalSound($cat); // Outputs: Meow!?>このコードでは、
makeAnimalSound
関数はAnimal
クラスのオブジェクトを引数として取る。そのため、Dog
クラスとCat
クラスのオブジェクトも引数として受け取ることができるぜ!これがポリモーフィズムの力だね!どうだ、理解できたかな?なのだぜ!
おお、すばらしいですね。オブジェクト指向プログラムのサンプルの伝統芸「アニマルクラスを継承した犬クラスと猫クラスがあり、鳴けといったら犬はワン!と鳴き、猫はニャー!と鳴く」のプログラムを使って教えてくれました。過去のインターネットの学習してるだけはありますね。ただこの説明って、あまりにも例が例すぎて具体的に使うにはどうしたらいいか、わからなくないですか?実際にこんなプログラムを書くことがないから、使うイメージがわかないんですよね。少なくとも僕が初学者の頃は何を言ってるかはわかったけど、ポリモーフィズムの利点として理解することはできませんでした。
なので、今回は突っ込んで質問してみることにしました。
ごえもん:
先生!何を言っているかは理解できますが、僕はビーストテイマーではないのでこんなプログラムを書くことはありません。また、利点についても理解できませんでした。もう少し、実用的な例を用いて、どういう利点があるかも教えてください!
チャッピー先生:
あ、ごめんね!ビーストテイマーじゃないんだね、わかったわかった。もう少し実用的で、利点が分かりやすい例を考えてみよう!では、PHPでウェブアプリケーションを開発する際の例を考えてみよう。例えば、データベースにアクセスするためのクラスがあり、そのクラスがMySQLやSQLiteなど、異なるデータベースに対応できるようにすることを考えてみるね。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 <?phpinterface DatabaseInterface {public function connect($host, $user, $pass, $dbname);public function query($sql);}class MySQLDatabase implements DatabaseInterface {public function connect($host, $user, $pass, $dbname) {// MySQLに接続するコードecho "Connected to MySQL Database\n";}public function query($sql) {// MySQLでクエリを実行するコードecho "Executing query on MySQL: $sql\n";}}class SQLiteDatabase implements DatabaseInterface {public function connect($host, $user, $pass, $dbname) {// SQLiteに接続するコードecho "Connected to SQLite Database\n";}public function query($sql) {// SQLiteでクエリを実行するコードecho "Executing query on SQLite: $sql\n";}}class MyApp {private $db;public function __construct(DatabaseInterface $db) {$this->db = $db;}public function run() {$this->db->connect('localhost', 'user', 'pass', 'dbname');$this->db->query('SELECT * FROM table');}}// MySQLを使用した場合$mysqlApp = new MyApp(new MySQLDatabase());$mysqlApp->run(); // Outputs: Connected to MySQL Database// Executing query on MySQL: SELECT * FROM table// SQLiteを使用した場合$sqliteApp = new MyApp(new SQLiteDatabase());$sqliteApp->run(); // Outputs: Connected to SQLite Database// Executing query on SQLite: SELECT * FROM table?>この例では、
DatabaseInterface
インターフェースを使用して、MySQLとSQLiteの2つの異なるデータベースクラスを定義しているぜ。そして、MyApp
クラスでは、このインターフェースを型として使用しており、どちらのデータベースクラスでも同じように扱うことができるんだ。ポリモーフィズムの利点は以下の通りだね:
- コードの再利用性:同じインターフェースを共有する異なるクラスのオブジェクトを、同じように扱うことができる。
- 拡張性:新しいデータベースクラスを追加する際に、既存のコードを変更することなく、新しいクラスを組み込むことができる。
- メンテナンス性:コードの変更や拡張が容易で、バグの発生リスクが低くなる。
どうだい、これでポリモーフィズムの利点が理解できたかな?なのだぜ!
教えてくれる内容は先程のワンニャーよりも大分現実にあり得るプログラムになり、わかりやすくなりました。connectとqueryという共通のメソッドを持つインタフェースDatabaseを定義し、それらの実装としてMySQLとSQLiteがある、というものですね。PostgreSQLを追加したくなったら、同じDatabaseインタフェースを使って簡単に拡張できるぞ、というものです。
ただ、これも正直ポリモーフィズムという概念を理解できてないレベルの初学者にとっては、良い例なのか、と言われると微妙です。そもそもこういう部分はWebアプリケーションフレームワーク使ってればほぼ間違いなく実装されているじゃないですか。これでデータベースを追加できるぞ!とか言われても、それってフレームワークの仕事ですよね?という感じで、これまた仕事で実装する機会はまずないでしょう。
なので、更に文句を言ってみることにします。
ごえもん:
先生!これってフレームワークで実装される部分ですよね。こんなプログラムを実装する部分を任せる先輩がいるとしたら、いくらなんでも僕に挑戦させすぎです。もっと、実際にwebサービスの一機能を開発する際に出てきそうな例を使って教えてください。
チャッピー先生:
ああ、なるほど。フレームワークが提供する機能の外で、具体的かつ一般的なシチュエーションを考えてみますね。例えば、eコマースサイトで商品のレコメンデーション機能を実装する場合、異なるレコメンデーションアルゴリズムを使用することが考えられます。一つはユーザーの購入履歴に基づくもの、もう一つは人気の商品に基づくもの、といった具体的なシチュエーションが考えられます。
ポリモーフィズムを用いると、異なるレコメンデーションアルゴリズムを同一のインターフェースで扱うことができ、新しいアルゴリズムの追加や既存のアルゴリズムの変更も容易になります。
12345678910111213141516171819202122232425262728293031323334353637383940 <?phpinterface RecommenderInterface {public function recommend(array $products, int $userId): array;}class HistoryBasedRecommender implements RecommenderInterface {public function recommend(array $products, int $userId): array {// ユーザーの購入履歴に基づいて商品をレコメンドecho "Recommending based on user's purchase history\n";return $products; // ここではサンプルのため、入力された商品をそのまま返します}}class PopularityBasedRecommender implements RecommenderInterface {public function recommend(array $products, int $userId): array {// 商品の人気度に基づいて商品をレコメンドecho "Recommending based on product popularity\n";return $products; // ここではサンプルのため、入力された商品をそのまま返します}}class RecommendationService {private $recommender;public function __construct(RecommenderInterface $recommender) {$this->recommender = $recommender;}public function getRecommendations(int $userId) {$products = ["Product A", "Product B", "Product C"];return $this->recommender->recommend($products, $userId);}}$service1 = new RecommendationService(new HistoryBasedRecommender());$recommendations1 = $service1->getRecommendations(1); // Outputs: Recommending based on user's purchase history$service2 = new RecommendationService(new PopularityBasedRecommender());$recommendations2 = $service2->getRecommendations(1); // Outputs: Recommending based on product popularity?>この例では、RecommenderInterfaceを実装したHistoryBasedRecommenderとPopularityBasedRecommenderを用意し、RecommendationServiceではこれらのオブジェクトを動的に切り替えて利用できます。
このように、ポリモーフィズムを利用することで、柔軟で拡張性のあるコードを書くことができます。実際の開発では、どのようなアルゴリズムや戦略を選択するかによって、利用するクラスを切り替えることができるでしょう。なのだぜ!
かなり現実的に実装する可能性のある例を用いて教えてくれました。これであれば、たとえば「あなたと似たユーザーの購入記録からレコメンドする」という機能を追加する場合なども同じような仕組みでいけそうだ、と理解が進みそうです。また、この例以外にも何回か「他にはありますか?」と聞いてみたのですが、聞けば聞くだけ教えてくれました。
チャッピー先生は偉大だった
今回ChatGPTを活用してプログラムの学習をやってみましたが、これは素晴らしいと思いました。何が素晴らしいかって、わからない部分があったら、自分が納得いくまでとことん質問していいところです。また、どれだけ質問の内容が低レベルであってもいいわけです。ChatGPTは「テメーそれ以上は自分の頭で考えろ!」とサジを投げることもありませんし、わからない部分を可能な限り解釈してそのレベルに合わせて教えてくれます。完全に自分専用に最適化された学習ができるため、非常に画期的な勉強方法であると言えます。プログラムだけではなく、ありとあらゆる学習において、この勉強方法は効果的です。あまりにも画期的すぎて勉強においては人間の先生要らなくなっちゃうんじゃないか?と心配になりました。改めて生成系AIの凄さを感じます。
ただなんかチャッピー先生、最初は「わかるかな?やってみよう!なのだぜ!」っていうノリノリな口調だったのに、最後の方は「これらのオブジェクトを動的に切り替えて利用できます。」とか言ってて、自分のキャラを完全に忘れてしまっています。ここは今後のAIの発展での解決が望まれます。念のため、最後にチャッピー先生がちゃんと最初の設定を覚えてるか確認してみました。
本当は「最近食生活に気を遣っているからね!」みたいに言ってほしかったのですが、なんかエクササイズで痩せたみたいです。
アライドアーキテクツでVPoPをしています。おもにダイエットに関する話を書きます。たまにサービス開発において大事だと思っていることを書いたりします。