WeCodeAcademy

Step 3: Store books behind an interface

Define a BookRepository interface: add a book, find by ISBN, search by text, list all. Then write InMemoryBookRepository using a LinkedHashMap (keeps insertion order). The rest of the application will only know the interface, so later you can swap in a database version without touching the rules. Optional makes "maybe there is no such book" explicit instead of returning null.

ExampleJava
import java.util.*;

public class Main {
  static class Book {
    final String isbn, title, author;
    boolean available = true;
    Book(String isbn, String title, String author) { this.isbn = isbn; this.title = title; this.author = author; }
    public String toString() { return title + (available ? "" : " (on loan)"); }
  }

  // The contract: the rest of the app depends on this, not on how books are stored.
  interface BookRepository {
    void add(Book book);
    Optional<Book> findByIsbn(String isbn);
    List<Book> search(String text);
    List<Book> all();
  }

  static class InMemoryBookRepository implements BookRepository {
    private final Map<String, Book> books = new LinkedHashMap<>();

    public void add(Book book) { books.put(book.isbn, book); }

    public Optional<Book> findByIsbn(String isbn) { return Optional.ofNullable(books.get(isbn)); }

    public List<Book> search(String text) {
      String t = text.toLowerCase();
      List<Book> result = new ArrayList<>();
      for (Book b : books.values()) {
        if (b.title.toLowerCase().contains(t) || b.author.toLowerCase().contains(t)) result.add(b);
      }
      return result;
    }

    public List<Book> all() { return new ArrayList<>(books.values()); }
  }

  public static void main(String[] args) {
    BookRepository repo = new InMemoryBookRepository();
    repo.add(new Book("1", "Clean Code", "Robert Martin"));
    repo.add(new Book("2", "The Pragmatic Programmer", "Hunt and Thomas"));
    repo.add(new Book("3", "Refactoring", "Martin Fowler"));

    System.out.println(repo.all().size() + " books");
    System.out.println(repo.findByIsbn("2").map(b -> b.title).orElse("not found"));
    System.out.println(repo.findByIsbn("9").map(b -> b.title).orElse("not found"));
    System.out.println(repo.search("martin"));
  }
}
Output
3 books
The Pragmatic Programmer
not found
[Clean Code, Refactoring]

findByIsbn("9") returned the fallback text because the Optional was empty. Search is case-insensitive and matches the title or the author.

Quick check

What does returning Optional<Book> tell the caller?

Finish the quick check (and any code exercise) to complete this lesson, or mark it done yourself.

Tip: use the left and right arrow keys to move between lessons.