生成AIで設計書からプログラミング。

こんにちは、皆様。プログラミングの世界は常に進化していますね。私が最近興味を持っているのはAIの力を借りてコーディングを行うことです。特に、OpenAIのChatGPTというAIと対話しながらプログラムを生成するアプローチに私は驚いています。

私はある日、設計書をChatGPTに渡し、JpaRepositoryの実装を頼むことにしました。具体的には、Userというエンティティと、それを操作するUserServiceとUserRepositoryを作成するという課題です。

この課題は、ドメイン駆動設計(DDD)に基づく設計を含んでおり、私がChatGPTに要求した設計書は、エンティティ、ドメインサービス、JPAエンティティ、そしてJPAリポジトリという4つの主要な部分をカバーしています。

AIを利用して実装を進める過程で、ChatGPTの機能がどれほど強力かを目の当たりにしました。JpaRepositoryの実装に必要なJavaとSpring Bootのコードはすべて問題なく生成されました。


プロンプト

# 指示
以下の設計書からJavaのプログラムを完全な形で出力してください。フレームワークはSpringBootになります。動作確認用のJunitプログラムも出力してください。

# 設計書

## エンティティ
クラス: User

説明
Userエンティティはユーザー情報を表します。

プロパティ
- String name: ユーザー名

コンストラクター
- User(String name): 指定された名前で新しいUserオブジェクトを作成します。

メソッド
- String getName(): ユーザー名を返します。

## ドメインサービス
クラス: UserService

説明
- UserServiceはUserに関するサービスを提供します。このサービス内ではドメインのUserとinfra.Userのオブジェクトマッピングも行います。

プロパティ
- UserRepository :  @Autowired でDI

メソッド
- void saveUser(User user): ユーザーをデータベースに保存します。
- User findUserByName(String name): 指定された名前のユーザーをデータベースから検索して返します。

## JPAエンティティ
パッケージ: infra
クラス: User

説明
- UserRepositoryはJPA Repositoryです。@Entityアノテーションを利用

プロパティ
- Long id: 自動生成ID
- String name: ユーザー名

## JPAリポジトリ
パッケージ: infra
インターフェース: UserRepository implements JpaRepository<User, Long>

説明
- UserRepositoryはJPA Repositoryです。@Repositoryアノテーションを利用

メソッド
- void save(infra.User user): ユーザーをJPAを使用してデータベースに保存します。
- User findByName(String name): 指定された名前のユーザーをJPAを使用してデータベースから検索して返します。

ChatGPTの回答

以下に、JavaとSpring Bootを使用して要求されたコードを提供します。

まず、ドメインエンティティの実装から始めます:

package domain;

public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

package infra;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    protected User() { }

    public User(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

次に、JPAエンティティとJPAリポジトリの実装を提供します:

package infra;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    protected User() { }

    public User(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}
package infra;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    User findByName(String name);
}

最後に、ドメインサービスを提供します:

package domain;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import infra.UserRepository;
import infra.User as InfraUser;

@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void saveUser(User user) {
        userRepository.save(new InfraUser(user.getName()));
    }

    public User findUserByName(String name) {
        InfraUser infraUser = userRepository.findByName(name);
        return new User(infraUser.getName());
    }
}

次にJunitテストケースを作成します。ただし、モックの動作は具体的な実装に依存するため、以下は参考例です:

package domain;

import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import infra.UserRepository;
import infra.User as InfraUser;

@SpringBootTest
public class UserServiceTest {

    @InjectMocks
    UserService userService;

    @Mock
    UserRepository userRepository;

    @Test
    public void testSaveUser() {
        User user = new User("testUser");
        userService.saveUser(user);
        Mockito.verify(userRepository, Mockito.times(1)).save(Mockito.any(InfraUser.class));
    }

    @Test
    public void testFindUserByName() {
        String testName = "testUser";
        Mockito.when(userRepository.findByName(testName)).thenReturn(new InfraUser(testName));
        User user = userService.findUserByName(testName);
        Mockito.verify(userRepository, Mockito.times(1)).findByName(testName);
        assert(user.getName().equals(testName));
    }
}

もちろん、このプロセスは人間の手が全く介在しないわけではありません。AIが生成したコードを確認し、修正や最適化を行うという手間は必要です。しかし、設計からコード生成までの一部をAIに任せることで、我々プログラマーはより高度な課題に集中できるようになるでしょう。

さらに驚いたのは、ChatGPTが作成したJunitテストケースです。モックを使ってUserRepositoryの動作を模擬し、依存関係を注入してテスト中に正しく呼び出されていることを確認しています。これにより、生成されたコードの品質を保証する手段も確保されました。

まとめると、ChatGPTは非常に強力なツールであり、それを効果的に使うことで、プログラミングの作業を効率化し、より高度な課題に焦点を当てることができるでしょう。もちろん、AIがすべてを解決するわけではありませんが、私たちのパートナーとして、より大きな可能性を広げてくれることでしょう。

今後も、ChatGPTを使ったプログラミングについて、さらに深く探求していきたいと思います。皆さんも、AIと共に新しいプログラミングの世界を体験してみてはいかがでしょうか。