実践:OOP

このレッスンはPhase 3のハンズオン実践で、図書管理システムを通じてOOP知識を定着させます。

プロジェクト要件

図書管理システムを設計し、以下をサポート:

クラス設計

エンティティクラス

JAVA
// Bookクラス
public class Book {
    private String isbn;
    private String title;
    private String author;
    private boolean borrowed;
    
    public Book(String isbn, String title, String author) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.borrowed = false;
    }
    
    // getter/setter
    public String getIsbn() { return isbn; }
    public String getTitle() { return title; }
    public String getAuthor() { return author; }
    public boolean isBorrowed() { return borrowed; }
    public void setBorrowed(boolean borrowed) { this.borrowed = borrowed; }
    
    @Override
    public String toString() {
        return String.format("[%s] %s - %s (%s)", isbn, title, author, borrowed ? "貸出中" : "貸出可能");
    }
}

// Readerクラス
public class Reader {
    private String id;
    private String name;
    private List<Book> borrowedBooks;
    
    public Reader(String id, String name) {
        this.id = id;
        this.name = name;
        this.borrowedBooks = new ArrayList<>();
    }
    
    public String getId() { return id; }
    public String getName() { return name; }
    public List<Book> getBorrowedBooks() { return borrowedBooks; }
    
    public void borrowBook(Book book) {
        borrowedBooks.add(book);
    }
    
    public void returnBook(Book book) {
        borrowedBooks.remove(book);
    }
    
    @Override
    public String toString() {
        return name + " (ID:" + id + ", 貸出:" + borrowedBooks.size() + ")";
    }
}

例外クラス

JAVA
// 書籍がすでに貸出中の例外
public class BookAlreadyBorrowedException extends Exception {
    public BookAlreadyBorrowedException(String message) {
        super(message);
    }
}

// 書籍が貸出中でない例外
public class BookNotBorrowedException extends Exception {
    public BookNotBorrowedException(String message) {
        super(message);
    }
}

// 読者が見つからない例外
public class ReaderNotFoundException extends Exception {
    public ReaderNotFoundException(String message) {
        super(message);
    }
}
▶ 試してみよう

インターフェース

JAVA
// 書籍管理インターフェース
public interface BookManager {
    void addBook(Book book);
    void removeBook(String isbn);
    Book findBook(String isbn);
    List<Book> getAllBooks();
}

// 読者管理インターフェース
public interface ReaderManager {
    void addReader(Reader reader);
    void removeReader(String id);
    Reader findReader(String id);
    List<Reader> getAllReaders();
}

管理クラス

JAVA
import java.util.*;

// Libraryクラス
public class Library implements BookManager, ReaderManager {
    private Map<String, Book> books;
    private Map<String, Reader> readers;
    
    public Library() {
        this.books = new HashMap<>();
        this.readers = new HashMap<>();
    }
    
    // 書籍管理
    @Override
    public void addBook(Book book) {
        books.put(book.getIsbn(), book);
        System.out.println("書籍追加: " + book.getTitle());
    }
    
    @Override
    public void removeBook(String isbn) {
        Book book = books.remove(isbn);
        if (book != null) {
            System.out.println("書籍削除: " + book.getTitle());
        } else {
            System.out.println("書籍が見つかりません");
        }
    }
    
    @Override
    public Book findBook(String isbn) {
        return books.get(isbn);
    }
    
    @Override
    public List<Book> getAllBooks() {
        return new ArrayList<>(books.values());
    }
    
    // 読者管理
    @Override
    public void addReader(Reader reader) {
        readers.put(reader.getId(), reader);
        System.out.println("読者追加: " + reader.getName());
    }
    
    @Override
    public void removeReader(String id) {
        Reader reader = readers.remove(id);
        if (reader != null) {
            System.out.println("読者削除: " + reader.getName());
        } else {
            System.out.println("読者が見つかりません");
        }
    }
    
    @Override
    public Reader findReader(String id) {
        return readers.get(id);
    }
    
    @Override
    public List<Reader> getAllReaders() {
        return new ArrayList<>(readers.values());
    }
    
    // 書籍貸出
    public void borrowBook(String isbn, String readerId) 
            throws BookAlreadyBorrowedException, ReaderNotFoundException {
        Book book = books.get(isbn);
        if (book == null) {
            System.out.println("書籍が見つかりません");
            return;
        }
        
        if (book.isBorrowed()) {
            throw new BookAlreadyBorrowedException("書籍はすでに貸出中: " + book.getTitle());
        }
        
        Reader reader = readers.get(readerId);
        if (reader == null) {
            throw new ReaderNotFoundException("読者が見つかりません: " + readerId);
        }
        
        book.setBorrowed(true);
        reader.borrowBook(book);
        System.out.println(reader.getName() + "が" + book.getTitle() + "を借りました");
    }
    
    // 書籍返却
    public void returnBook(String isbn, String readerId) 
            throws BookNotBorrowedException, ReaderNotFoundException {
        Book book = books.get(isbn);
        if (book == null) {
            System.out.println("書籍が見つかりません");
            return;
        }
        
        if (!book.isBorrowed()) {
            throw new BookNotBorrowedException("書籍は貸出中ではありません: " + book.getTitle());
        }
        
        Reader reader = readers.get(readerId);
        if (reader == null) {
            throw new ReaderNotFoundException("読者が見つかりません: " + readerId);
        }
        
        book.setBorrowed(false);
        reader.returnBook(book);
        System.out.println(reader.getName() + "が" + book.getTitle() + "を返却しました");
    }
}

メインプログラム

JAVA
import java.util.Scanner;

public class LibrarySystem {
    private static Library library = new Library();
    private static Scanner scanner = new Scanner(System.in);
    
    public static void main(String[] args) {
        // データ初期化
        initData();
        
        // メインループ
        int choice;
        do {
            showMenu();
            choice = scanner.nextInt();
            scanner.nextLine();  // 改行を消費
            
            switch (choice) {
                case 1: listBooks(); break;
                case 2: listReaders(); break;
                case 3: borrowBook(); break;
                case 4: returnBook(); break;
                case 5: addBook(); break;
                case 6: addReader(); break;
                case 0: System.out.println("さようなら!"); break;
                default: System.out.println("無効な選択");
            }
        } while (choice != 0);
        
        scanner.close();
    }
    
    private static void initData() {
        library.addBook(new Book("978-7-111-40701-0", "Javaプログラミング", "Bruce Eckel"));
        library.addBook(new Book("978-7-111-21382-6", "デザインパターン", "GoF"));
        library.addBook(new Book("978-7-115-22170-8", "アルゴリズム入門", "CLRS"));
        
        library.addReader(new Reader("R001", "Alice"));
        library.addReader(new Reader("R002", "Bob"));
    }
    
    private static void showMenu() {
        System.out.println("\n=== 図書管理システム ===");
        System.out.println("1. 書籍一覧");
        System.out.println("2. 読者一覧");
        System.out.println("3. 書籍貸出");
        System.out.println("4. 書籍返却");
        System.out.println("5. 書籍追加");
        System.out.println("6. 読者追加");
        System.out.println("0. 終了");
        System.out.print("選択してください: ");
    }
    
    private static void listBooks() {
        System.out.println("\n--- 書籍一覧 ---");
        for (Book book : library.getAllBooks()) {
            System.out.println(book);
        }
    }
    
    private static void listReaders() {
        System.out.println("\n--- 読者一覧 ---");
        for (Reader reader : library.getAllReaders()) {
            System.out.println(reader);
        }
    }
    
    private static void borrowBook() {
        System.out.print("ISBNを入力: ");
        String isbn = scanner.nextLine();
        System.out.print("読者IDを入力: ");
        String readerId = scanner.nextLine();
        
        try {
            library.borrowBook(isbn, readerId);
        } catch (BookAlreadyBorrowedException | ReaderNotFoundException e) {
            System.out.println("エラー: " + e.getMessage());
        }
    }
    
    private static void returnBook() {
        System.out.print("ISBNを入力: ");
        String isbn = scanner.nextLine();
        System.out.print("読者IDを入力: ");
        String readerId = scanner.nextLine();
        
        try {
            library.returnBook(isbn, readerId);
        } catch (BookNotBorrowedException | ReaderNotFoundException e) {
            System.out.println("エラー: " + e.getMessage());
        }
    }
    
    private static void addBook() {
        System.out.print("ISBNを入力: ");
        String isbn = scanner.nextLine();
        System.out.print("タイトルを入力: ");
        String title = scanner.nextLine();
        System.out.print("著者を入力: ");
        String author = scanner.nextLine();
        
        library.addBook(new Book(isbn, title, author));
    }
    
    private static void addReader() {
        System.out.print("読者IDを入力: ");
        String id = scanner.nextLine();
        System.out.print("名前を入力: ");
        String name = scanner.nextLine();
        
        library.addReader(new Reader(id, name));
    }
}

出力例

TEXT
=== 図書管理システム ===
1. 書籍一覧
2. 読者一覧
3. 書籍貸出
4. 書籍返却
5. 書籍追加
6. 読者追加
0. 終了
選択してください: 1

--- 書籍一覧 ---
[978-7-111-40701-0] Javaプログラミング - Bruce Eckel (貸出可能)
[978-7-111-21382-6] デザインパターン - GoF (貸出可能)
[978-7-115-22170-8] アルゴリズム入門 - CLRS (貸出可能)

選択してください: 3
ISBNを入力: 978-7-111-40701-0
読者IDを入力: R001
AliceがJavaプログラミングを借りました

設計のポイント

説明
カプセル化 プライベート属性、getter/setterでアクセス
継承 インターフェース継承(BookManager、ReaderManager)
ポリモーフィズム インターフェース参照が実装を指す
例外処理 ビジネスエラー用のカスタム例外
コレクション 書籍と読者にMap、貸出記録にList

❓ よくある質問

Q システムを拡張するにはどうすればいいですか?
A 書籍カテゴリ、貸出履歴、延滞料金などを追加してください。
Q データを永続化するにはどうすればいいですか?
A ファイルIOまたはデータベースを使用してデータを保存してください。
Q マルチスレッディングをサポートするにはどうすればいいですか?
A 同期化(synchronized)または並行コレクション(ConcurrentHashMap)を使用してください。

📖 まとめ

📝 演習

  1. 機能拡張: 書籍カテゴリとカテゴリベースの検索を追加
  2. 貸出履歴: 貸出履歴を記録し、履歴の表示をサポート
  3. データ永続化: データをファイルに保存し、プログラム起動時に読み込み

次のレッスン

次のレッスンでは、Phase 4に移り、コレクションフレームワーク概要を学びます — Javaコレクションシステムを理解します。

100%