하위 태스크 1

패키지 예제 분석

s0701 , s0702 의 패키지 구조 분석

s0701 패키지는 s0701.house 서브 패키지와 s0701.house.person 서브 패키지를 포함한다. 두 서브 패키지에 속한 모든 클래스와 클래스의 메서드는 public 접근 제어자를 사용했기 때문에 소속된 패키지 외부에서 접근할 수 있다.

s0702 패키지의 디렉터리 구조는 s0701 패키지와 같다. 서브 패키지를 포함해서 s0702 패키지에 속한 모든 클래스는 public 접근 제어자가 적용되었다. 단, s0702 패키지의 Sample0702_01 클래스의 모든 멤버는 private 접근 제어자가 적용되어서 다른 클래스에서 멤버에 접근하지 못하고, s0702.house 패키지의 HousePark 클래스는 protected 접근 제어자가 적용된 객체 변수가 있어서 동일 패키지에 있거나 HousePark 클래스를 상속한 클래스만 해당 객체 변수에 접근할 수 있다.

하위 태스크 2

프로젝트 패키지 설계

model , service , util , exception 등 설계

  • model 패키지: Book.java, Author.java, Store.java
  • service 패키지: SaleService.java, ExchangeService.java, UserService.java
  • util 패키지: Validation.java, Formatter.java, Filter.java
  • exception 패키지: NotEnoughQuantityException, UnknownBookException

하위 태스크 3

접근 제어 실험

public, protected, default, private 테스트

Foo 클래스는 foo 패키지에 있고 Bar 클래스는 bar 패키지에 있어서, Foo 클래스와 Bar 클래스는 서로 다른 패키지에 있다고 전제한다.

Bar 클래스의 접근 제어자에 따른 동작은 다음과 같다.

/* 접근 제어자 */ class Bar {
}
  • public으로 설정: Foo 클래스에서 Bar 클래스를 import 하여 사용할 수 있다.
  • default로 설정: Foo 클래스는 외부 패키지에 속하기 때문에 Bar 클래스를 import 하여 사용할 수 없다.
  • protected로 설정: modifier protected not allowed here 컴파일 오류가 발생한다.
  • private로 설정: modifier private not allowed here 컴파일 오류가 발생한다.

하위 태스크 4 ~ 6

사용자 정의 예외 작성

도메인 전용 예외 클래스 구현

컬렉션/스트림 로직 구현

필터링/정렬/집계 기능 구현

통합 미니 프로젝트 완성

패키지/예외/스트림 결합한 프로그램 완성

다음은 도서 관리 프로그램을 구현한 예제다. model 패키지, service 패키지, exception 패키지로 구분했으며 기준은 다음과 같다.

  • model 패키지: 프로그램이 관리하는 대상을 추상화한 클래스를 포함한다. 이 경우 도서를 의미한다.
  • service 패키지: 프로그램이 관리하는 대상의 저장소와 몇 가지 관리 연산을 포함하는 클래스를 포함한다.
  • exception 패키지: 연산 중에 발생할 수 있는 예외 클래스를 포함한다.

Sample.java:

import model.Book;
import service.BookService;
 
import java.util.Comparator;
import java.util.List;
 
public class Sample {
    public static void main(String[] args) {
        BookService bookService = new BookService();
 
        bookService.addBook("9791163034872", "점프 투 자바", "박응용");
        bookService.addBook("9791163031734", "파이썬 생활 프로그래밍", "김창현");
        bookService.addBook("9791187370703", "C 언어 입문", "김성엽");
 
        // 스트림과 정렬 사용 사례
        List<Book> booksSortedByTitle = bookService.getBooks().stream().sorted(Comparator.comparing(Book::getTitle).reversed()).toList();
        for (Book book : booksSortedByTitle) {
            System.out.println(book.getTitle());
        }
 
        // 스트림과 필터링 사용 사례
        List<Book> booksFilteredByAuthor = bookService.getBooks().stream().filter((b) -> b.getAuthor().equals("박응용")).toList();
        for (Book book : booksFilteredByAuthor) {
            System.out.println(book.getTitle());
        }
 
        // 스트림과 집계 기능 사용 사례
        long booksCount = bookService.getBooks().stream().count();
        System.out.println(booksCount);
    }
}

model.Book.java:

package model;
 
public class Book {
    private String isbn;
    private String title;
    private String author;
 
    public Book(String isbn, String title, String author) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
    }
 
    public String getIsbn() {
        return isbn;
    }
 
    public String getTitle() {
        return title;
    }
 
    public String getAuthor() {
        return author;
    }
}

service.BookService.java:

package service;
 
import exception.InvalidAuthorException;
import exception.InvalidIsbnException;
import exception.InvalidTitleException;
import model.Book;
 
import java.util.HashMap;
import java.util.List;
 
public class BookService {
    private final HashMap<String, Book> bookRegistry = new HashMap<>();
 
    public Book getBook(String isbn) {
        return bookRegistry.get(isbn);
    }
 
    public List<Book> getBooks() {
        return bookRegistry.values().stream().toList();
    }
 
    public Book addBook(String isbn, String title, String author) throws InvalidIsbnException, InvalidTitleException, InvalidAuthorException {
        if (!validateISBN(isbn)) {
            throw new InvalidIsbnException();
        }
        if (!validateTitle(title)) {
            throw new InvalidTitleException();
        }
        if (!validateAuthor(author)) {
            throw new InvalidAuthorException();
        }
        
		Book newBook = new Book(isbn, title, author);
		bookRegistry.put(isbn, newBook);
		return newBook;
    }
 
    public void removeBook(String isbn) {
        bookRegistry.remove(isbn);
    }
 
    public boolean validateISBN(String isbn) {
        return isbn.length() == 13;
    }
 
    public boolean validateTitle(String title) {
        return !title.isEmpty();
    }
 
    public boolean validateAuthor(String author) {
        return !author.isEmpty();
    }
}

exception.InvalidAuthorException.java:

package exception;
 
public class InvalidAuthorException extends RuntimeException {
    public InvalidAuthorException() {
        super("작가의 이름은 한 글자 이상으로 설정해야 합니다.");
    }
}

exception.InvalidIsbnException.java:

package exception;
 
public class InvalidIsbnException extends RuntimeException {
    public InvalidIsbnException() {
        super("ISBN은 13글자로 설정해야 합니다.");
    }
}

exception.InvalidTitleException.java:

package exception;
 
public class InvalidTitleException extends RuntimeException {
    public InvalidTitleException() {
        super("제목은 한 글자 이상으로 설정해야 합니다.");
    }
}