병아리의 코딩 일기

Java 인터페이스를 활용해보자! 도서 관리 시스템 예제 본문

자바 Java

Java 인터페이스를 활용해보자! 도서 관리 시스템 예제

oilater 2023. 7. 22. 06:45

Book

package inter;

public class Book {
	// 아래에 Getter를 써주니 멤버 변수(?) 부분은 private으로 감춰줌.
	// 이제 Book의 속성들에 직접 접근하기 위해서는 getter, setter를 이용해야 함.
	private String isbn;
	private String title;
	private String author;
	private String publisher;
	private int price;
	private String desc;
	
	// 기본 생성자를 쓰지 않을건데 굳이 선언하는 이유?
	public Book() {}
	
	// Book을 생성할 때 써주는 파라미터(매개변수)의 값들을 현재 Book 클래스의 각 변수들에 할당함(초기화)
	public Book(String isbn, String title, String author, String publisher, int price, String desc) {
		this.isbn = isbn;
		this.title = title;
		this.author = author;
		this.publisher = publisher;
		this.price = price;
		this.desc = desc;
	}
	
	//Getter, Setter
	public String getIsbn() {
		return isbn;
	}

	public void setIsbn(String isbn) {
		this.isbn = isbn;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getPublisher() {
		return publisher;
	}

	public void setPublisher(String publisher) {
		this.publisher = publisher;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public String getDesc() {
		return desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}
	
	@Override
	public String toString() {
		return isbn + "    |    " + title + "    |    " + author + "    |    " + publisher + "    |    " + price + "    |    " + desc;
	}
	
	
}

Magazine

package inter;

public class Magazine extends Book {
	// Magazine의 추가 속성들
	private int year;
	private int month;
	
	// 기본 생성자
	public Magazine() {};
	
	// 생성자
	public Magazine(String isbn, String title, String author, String publisher, int price, String desc, int year, int month) {
		// Magazine은 Book을 상속받아 확장하는 클래스이기 때문에 Book의 속성들도 모두 가지고 있다.
		// 이때 생성자에서 초기화 할때는 super를 이용해서 부모의 속성들을 넣어주면 되고
		// 추가적으로 Magazine의 속성을 초기화해준다.
		super(isbn, title, author, publisher, price, desc);
		this.year = year;
		this.month = month;
	}
	
	
	// getter, setter
	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public int getMonth() {
		return month;
	}

	public void setMonth(int month) {
		this.month = month;
	}
	
	@Override
	public String toString() {
		return this.getIsbn() + "    |    " + this.getTitle() + "    |    " + this.getAuthor() + "    |    " + this.getPublisher() + "    |    " + this.getPrice() + "    |    " + this.getDesc() + "    |    " + year + "    |    " + month;
	}
	
}

IBookManager (Interface)

package inter;

public interface IBookManager {
	// interface 는 상수의 경우 항상 public static final 이어야 하고,
	// 메서드의 경우 public abstract이어야 한다. 이 둘은 컴파일러가 자동으로 추가해주므로 생략 가능하다.
	// interface 에서 필요한 기능들을 선언해놓고 BookManagerImpl 에서 이 메서드를 구현할 것이다.
	void add(Book book);

	void remove(String isbn);

	Book[] getList();

	Book searchByIsbn(String isbn);

	Book[] searchByTitle(String title);

	Magazine[] getMagazines();

	Book[] getBooks();

	int getTotalPrice();

	double getPriceAvg();

}

BookManagerImpl

package inter;

import java.util.Arrays;

/*
 * 싱글톤을 구현하는 단계
 * 1. 현재 클래스 생성자의 접근 제한자를 private으로 설정한다. (이제 외부에서 현재 클래스를 생성할 수 없음)
 * 2. class 자체에 private을 붙이는 건 안되겠지? 아예 멤버들을 가져다 쓸 수 없으니까?
 * 3. IBookManager(부모)이나 BookManagerImpl 타입으로 이 클래스 객체가 들어갈 instance라는 변수를 하나 만들어줌 
 *     // 바로 static으로 넘겨줄 수 있어야 하니까 역시 static으로 만들어준다. 근데 왜 private일까? (이 클래스에서만 접근하게 하도록 이겠지?)
 * 4. static 으로 getInstance 라는 정적 메서드 생성 후 instance에 본 클래스 객체를 담아서 return 해준다. 
 * 
 * 싱글톤의 장점
 * 1. 메모리 절약 가능
 * 2. 클래스 간의 데이터 공유가 쉽 (전역으로 사용되는 인스턴스라 다른 클래스의 인스턴스들이 쉽게 접근할 수 있음)
 */

public class BookManagerImpl implements IBookManager {
	private static final int MAX_SIZE = 100;
	private int size = 0;
	private Book[] books = new Book[MAX_SIZE];
	// 싱글톤 (타입은 IBookManager로 해줌. BookManagerImpl로 해도 문제는 없음)
	private static IBookManager instance;

	// 생성자
	private BookManagerImpl() {
	};

	// 댜른 클래스에서 가져올 수 있도록 해야 하니까 static 으로 해줘야 함.
	public static IBookManager getInstance() {
		if (instance == null) {
			instance = new BookManagerImpl();
		}
		return instance;
	}

	public void add(Book book) {
		books[size++] = book;
		System.out.println("책이 추가되었습니다!");
	}

	public Book[] getList() {
		return Arrays.copyOf(books, size);
	}

	// books를 해줘도 될까? 되야 하는 거 아닌가? 그때 왜 getList()를 써야 나왔지 - 해결 books 는 애초에 많은 배열임
	public Book searchByIsbn(String isbn) {
		for (int i = 0; i < size; i++) {
			if (books[i].getIsbn().equals(isbn)) {
				return books[i];
			}
		}
		return null;
	}

	public Book[] searchByTitle(String title) {
		int count = 0;
		Book[] list = new Book[MAX_SIZE];
		for (int i = 0; i < size; i++) {
			if (books[i].getTitle().contains(title)) {
				list[count++] = books[i];
			}
		}
		return Arrays.copyOf(list, count);
	}

	public Magazine[] getMagazines() {
		int cnt = 0;
		Magazine[] arr = new Magazine[MAX_SIZE];
		for (int i = 0; i < size; i++) {
			if (books[i] instanceof Magazine) {
				// 여기서 궁금증 => 자식이 부모 타입을 가지게 되면 생기는 이점? 단지 다른 자식들과 함께 관리될 수 있다는 것 외에 다른 이점이 있을까?
				arr[cnt++] = (Magazine) books[i];
			}
		}
		return Arrays.copyOf(arr, cnt);
	}

	public Book[] getBooks() {
		int cnt = 0;
		Book[] arr = new Book[MAX_SIZE];
		for (int i = 0; i < size; i++) {
			if (!(books[i] instanceof Magazine)) {
				arr[cnt++] = books[i];
			}
		}
		return Arrays.copyOf(arr, cnt);
	}

	public int getTotalPrice() {
		int sum = 0;
		for (int i = 0; i < size; i++) {
			sum += books[i].getPrice();
		}
		return sum;
	}

	public double getPriceAvg() {
		return getTotalPrice() / size;
	}

	public void remove(String isbn) {
		int deleteIdx = 0;
		for (int i = 0; i < size; i++) {
			if (books[i].getIsbn().equals(isbn)) {
				deleteIdx = i;
			}
		}
		books[deleteIdx] = books[size - 1];
		books[--size] = null;
	}

}

BookTest (main)

package inter;

public class BookTest {

	public static void main(String[] args) {
		// 싱글톤 객체 생성
		IBookManager bm = BookManagerImpl.getInstance();
		
		// 도서 및 잡지 생성
		Book b1 = new Book("21424", "Java Pro", "김하나", "jaen.kr", 15000, "Java 기본 문법");
		Book b2 = new Book("21425", "Java Pro2", "김하나", "jaen.kr", 25000, "Java 응용");
		Book b3 = new Book("35355", "분석 설계", "소나무", "jaen.kr", 30000, "SW 모델링");
		Magazine m1 = new Magazine("45678", "월간 알고리즘", "김성현", "jaen.kr", 10000, "1월 알고리즘", 2021, 1);

		//도서 추가
		bm.add(b1);
		bm.add(b2);
		bm.add(b3);
		bm.add(m1);
		
		// 도서 전체 목록
		System.out.println("********* 도서 전체 목록 *********");
		Book[] entireList = bm.getList();
		for (Book b : entireList) {	
			System.out.println(b); // 클래스의 정보를 알려주는 미리 오버라이드 해놓은 toString 메서드가 실행됨
		}
		
		// 일반 도서 목록 
		System.out.println("********* 일반 도서 목록 *********");
		Book[] bList = bm.getBooks();
		for (Book b : bList) {
			System.out.println(b);
		}
		
		// 잡지 목록
		System.out.println("********* 잡지 목록 *********");
		Magazine[] mList = bm.getMagazines();
		for (Magazine m : mList) {
			System.out.println(m);
		}
		
		// 제목으로 도서 검색
		System.out.println("********* 도서 제목 포함 검색 : Java *********");
		Book[] searchedList = bm.searchByTitle("Java");
		for (Book b : searchedList) {
			System.out.println(b);
		}
		
		// 도서 가격 총합 및 평균
		System.out.printf("도서 가격 총합: %d%n", bm.getTotalPrice());
		System.out.printf("도서 가격 평균: %.1f", bm.getPriceAvg());
	}

}
728x90
반응형
LIST