図書館管理システムを作ってみるハンズオン！

# 図書館管理システム ハンズオン

## 目次
1. [システム概要](#システム概要)
2. [環境構築](#環境構築)
3. [基本クラスの実装](#基本クラスの実装)
4. [機能実装](#機能実装)
5. [動作確認](#動作確認)
6. [発展課題](#発展課題)

## システム概要

このハンズオンでは、図書館の基本的な業務をJavaで実装します。
以下の機能を持つシステムを段階的に構築していきます：

- 本の貸出/返却管理
- 会員情報の管理
- 蔵書検索機能

### クラス構成図
```
LibrarySystem
├── Book (書籍クラス)
├── Member (会員クラス)
├── BookManager (蔵書管理クラス)
├── MemberManager (会員管理クラス)
└── LibraryService (図書館サービスクラス)
```

## 環境構築

必要な環境：
- Java 11以上
- IDE（Eclipse/IntelliJ IDEA）
- JUnit 5（テスト用）

## 基本クラスの実装

### 1. Book クラス

```java
/**
 * 図書館の書籍を管理するクラス
 * ISBN、タイトル、著者、貸出状況などの情報を保持する
 */
public class Book {
    // 書籍を一意に識別するISBNコード
    private String isbn;
    // 書籍のタイトル
    private String title;
    // 著者名
    private String author;
    // 貸出可能かどうかのフラグ
    private boolean isAvailable;
    // 現在の貸出者のID（貸出中でない場合はnull）
    private String borrowedBy;
    // 返却期限日
    private LocalDate dueDate;
    
    /**
     * 書籍の新規作成を行うコンストラクタ
     * @param isbn ISBN番号
     * @param title 書籍タイトル
     * @param author 著者名
     */
    public Book(String isbn, String title, String author) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.isAvailable = true;  // 新規作成時は貸出可能状態
    }
    
    // Getterメソッド
    public String getIsbn() { return isbn; }
    public String getTitle() { return title; }
    public String getAuthor() { return author; }
    public boolean isAvailable() { return isAvailable; }
    public String getBorrowedBy() { return borrowedBy; }
    public LocalDate getDueDate() { return dueDate; }
    
    /**
     * 書籍の貸出処理を行う
     * @param memberId 貸出を行う会員のID
     * @param borrowDays 貸出日数
     * @throws IllegalStateException 既に貸出中の場合
     */
    public void borrowBook(String memberId, int borrowDays) {
        if (!isAvailable) {
            throw new IllegalStateException("この本は既に貸し出されています");
        }
        this.isAvailable = false;
        this.borrowedBy = memberId;
        this.dueDate = LocalDate.now().plusDays(borrowDays);
    }
    
    /**
     * 書籍の返却処理を行う
     */
    public void returnBook() {
        this.isAvailable = true;
        this.borrowedBy = null;
        this.dueDate = null;
    }
}
```

### 2. Member クラス

```java
/**
 * 図書館の会員を管理するクラス
 * 会員情報と貸出中の書籍リストを管理する
 */
public class Member {
    // 会員を一意に識別するID
    private String memberId;
    // 会員の名前
    private String name;
    // 会員のメールアドレス
    private String email;
    // 貸出中の書籍のISBNリスト
    private List<String> borrowedBooks;
    
    /**
     * 会員の新規作成を行うコンストラクタ
     * @param memberId 会員ID
     * @param name 会員名
     * @param email メールアドレス
     */
    public Member(String memberId, String name, String email) {
        this.memberId = memberId;
        this.name = name;
        this.email = email;
        this.borrowedBooks = new ArrayList<>();  // 空の貸出リストで初期化
    }
    
    // Getterメソッド
    public String getMemberId() { return memberId; }
    public String getName() { return name; }
    public String getEmail() { return email; }
    public List<String> getBorrowedBooks() { return new ArrayList<>(borrowedBooks); }
    
    /**
     * 書籍を借りた際の処理
     * @param isbn 借りる書籍のISBN
     */
    public void borrowBook(String isbn) {
        borrowedBooks.add(isbn);
    }
    
    /**
     * 書籍を返却した際の処理
     * @param isbn 返却する書籍のISBN
     */
    public void returnBook(String isbn) {
        borrowedBooks.remove(isbn);
    }
    
    /**
     * 現在借りている本の冊数を返す
     * @return 貸出中の書籍数
     */
    public int getBorrowedCount() {
        return borrowedBooks.size();
    }
}
```

### 3. BookManager クラス

```java
/**
 * 図書館の蔵書全体を管理するクラス
 * 書籍の追加、検索、在庫確認などの機能を提供する
 */
public class BookManager {
    // ISBNをキーとした書籍のマップ
    private Map<String, Book> books;
    
    /**
     * BookManagerの初期化を行うコンストラクタ
     */
    public BookManager() {
        this.books = new HashMap<>();
    }
    
    /**
     * 新しい書籍を蔵書に追加する
     * @param book 追加する書籍オブジェクト
     * @throws IllegalArgumentException 既に同じISBNの書籍が存在する場合
     */
    public void addBook(Book book) {
        if (books.containsKey(book.getIsbn())) {
            throw new IllegalArgumentException("この本は既に登録されています: " + book.getIsbn());
        }
        books.put(book.getIsbn(), book);
    }
    
    /**
     * ISBNから書籍を検索する
     * @param isbn 検索するISBN
     * @return 該当する書籍。存在しない場合はnull
     */
    public Book findByIsbn(String isbn) {
        return books.get(isbn);
    }
    
    /**
     * キーワードで書籍を検索する
     * タイトルまたは著者名に指定したキーワードを含む書籍を返す
     * @param keyword 検索キーワード
     * @return 検索結果の書籍リスト
     */
    public List<Book> searchBooks(String keyword) {
        return books.values().stream()
            .filter(book -> book.getTitle().contains(keyword) || 
                          book.getAuthor().contains(keyword))
            .collect(Collectors.toList());
    }
    
    /**
     * 現在貸出可能な書籍のリストを取得する
     * @return 貸出可能な書籍のリスト
     */
    public List<Book> getAvailableBooks() {
        return books.values().stream()
            .filter(Book::isAvailable)
            .collect(Collectors.toList());
    }
    
    /**
     * 登録されている全書籍の数を返す
     * @return 蔵書数
     */
    public int getTotalBookCount() {
        return books.size();
    }
}
```

### 4. MemberManager クラス

```java
/**
 * 図書館の会員を管理するクラス
 * 会員の追加、検索、削除などの機能を提供する
 */
public class MemberManager {
    // 会員IDをキーとした会員のマップ
    private Map<String, Member> members;
    
    /**
     * MemberManagerの初期化を行うコンストラクタ
     */
    public MemberManager() {
        this.members = new HashMap<>();
    }
    
    /**
     * 新しい会員を追加する
     * @param member 追加する会員オブジェクト
     * @throws IllegalArgumentException 既に同じIDの会員が存在する場合
     */
    public void addMember(Member member) {
        if (members.containsKey(member.getMemberId())) {
            throw new IllegalArgumentException("この会員IDは既に使用されています: " + member.getMemberId());
        }
        members.put(member.getMemberId(), member);
    }
    
    /**
     * 会員IDから会員を検索する
     * @param memberId 検索する会員ID
     * @return 該当する会員。存在しない場合はnull
     */
    public Member findById(String memberId) {
        return members.get(memberId);
    }
    
    /**
     * 会員を削除する
     * @param memberId 削除する会員のID
     * @throws IllegalStateException 貸出中の本がある場合
     */
    public void removeMember(String memberId) {
        Member member = findById(memberId);
        if (member == null) {
            throw new IllegalArgumentException("会員が見つかりません: " + memberId);
        }
        if (!member.getBorrowedBooks().isEmpty()) {
            throw new IllegalStateException("貸出中の本があるため削除できません");
        }
        members.remove(memberId);
    }
    
    /**
     * 登録されている全会員数を返す
     * @return 会員数
     */
    public int getTotalMemberCount() {
        return members.size();
    }
}
```

### 5. LibraryService クラス

```java
/**
 * 図書館の主要な業務ロジックを提供するサービスクラス
 * 書籍の貸出/返却、検索などの機能を統合的に管理する
 */
public class LibraryService {
    // 書籍管理用のマネージャー
    private BookManager bookManager;
    // 会員管理用のマネージャー
    private MemberManager memberManager;
    // 標準の貸出期間（日数）
    private static final int DEFAULT_BORROW_DAYS = 14;
    
    /**
     * LibraryServiceの初期化を行うコンストラクタ
     */
    public LibraryService() {
        this.bookManager = new BookManager();
        this.memberManager = new MemberManager();
    }
    
    /**
     * 書籍の貸出処理を行う
     * @param memberId 借りる会員のID
     * @param isbn 借りる書籍のISBN
     * @throws IllegalArgumentException 会員または書籍が存在しない場合
     * @throws IllegalStateException 書籍が貸出中の場合
     */
    public void borrowBook(String memberId, String isbn) {
        // 会員と書籍の存在確認
        Member member = memberManager.findById(memberId);
        Book book = bookManager.findByIsbn(isbn);
        
        if (member == null || book == null) {
            throw new IllegalArgumentException("会員または書籍が見つかりません");
        }
        
        // 貸出可能かの確認
        if (!book.isAvailable()) {
            throw new IllegalStateException("この本は現在貸出できません");
        }
        
        // 貸出処理の実行
        book.borrowBook(memberId, DEFAULT_BORROW_DAYS);
        member.borrowBook(isbn);
    }
    
    /**
     * 書籍の返却処理を行う
     * @param memberId 返却する会員のID
     * @param isbn 返却する書籍のISBN
     * @throws IllegalArgumentException 会員または書籍が存在しない場合
     * @throws IllegalStateException 別の会員が借りている場合
     */
    public void returnBook(String memberId, String isbn) {
        // 会員と書籍の存在確認
        Member member = memberManager.findById(memberId);
        Book book = bookManager.findByIsbn(isbn);
        
        if (member == null || book == null) {
            throw new IllegalArgumentException("会員または書籍が見つかりません");
        }
        
        // 返却権限の確認
        if (!memberId.equals(book.getBorrowedBy())) {
            throw new IllegalStateException("この本は別の会員が借りています");
        }
        
        // 返却処理の実行
        book.returnBook();
        member.returnBook(isbn);
    }
    
    /**
     * キーワードによる書籍検索を行う
     * @param keyword 検索キーワード
     * @return 検索結果の書籍リスト
     */
    public List<Book> searchBooks(String keyword) {
        return bookManager.searchBooks(keyword);
    }
    
    // Getterメソッド
    public BookManager getBookManager() { return bookManager