class Book {
private String name;
private Integer page;
/* 생성자 생략 */
}
이러한 클래스가 있다고 가정해보자.
일반적으로 Book
인스턴스를 만들려고한다면, 아래와 같은 코드로 새로운 인스턴스를 생성할 것이다.
Book book = new Book();
그런데 팩토리 패턴은 내가 직접 인스턴스를 생성하지 않는다.
인스턴스를 생성해주는 팩토리 클래스를 하나 만들고 인스턴스를 생성하는 것은 팩토리 클래스가 대신해주게 된다.
"팩토리 메서드 패턴", 말 그대로 공장에서 물건을 찍어내는 개념이라고 볼 수 있다.
팩토리 메서드 패턴은 그냥 인스턴스를 생성하는 것에 비해 다양한 장점들을 갖고 있는데, 이 장점들을 이야기하기 전에 먼저 예제 코드로 팩터리 메서드 패턴이 어떤 것인지 알아보자.
팩토리 메서드 패턴의 핵심은 인터페이스
이다.
"책"이라는 인터페이스를 상속받아서 "만화책", "소설책" 클래스를 만들고, "책 공장"이라는 인터페이스를 상속받아서 "만화책 공장", "소설책 공장" 팩토리 클래스를 만드는 것이다.
즉, 구현체는 달라지지만 동일한 인터페이스들을 상속받기 때문에 실질적인 기능은 동일하게 된다.
말로 하니 뭔가 어려운 것 같은데... 😂
바로 예제로 알아보자!
public interface Book {
String getTitle();
Integer getPage();
String getBookInfo();
}
여러 종류의 책이 상속받을 Book 인터페이스
이다.
이를 상속받아서 만화책, 소설책등등의 클래스를 만들 수 있다.
public class ComicBook implements Book {
private final String title = "체인쏘맨";
private final Integer page = 200;
@Override
public String getTitle() {
return title;
}
@Override
public Integer getPage() {
return page;
}
@Override
public String getBookInfo() {
return "만화책:" + title + "/p." + page;
}
}
Book 인터페이스
를 상속받아서 만든 ComicBook
이라는 클래스이다.
public interface BookFactory {
Book publishBook();
default void noticeToFan(String title) {
System.out.println(title + "이 발매 됐습니다!");
}
}
여러 종류의 책이 상속받을 Book 팩토리 인터페이스
이다.
이를 상속받아서 만화책 팩토리, 소설책 팩토리등등의 팩토리 클래스를 만들 수 있다.
그리고 필요에 따라서 팩토리 클래스들에서 공통적으로 사용될 default메서드 또한 구현해주었다. 서로 다른 구현체로써 팩토리 클래스들은 만들어질 것이지만, 공통적으로 사용해야하는 기능이 있다면 이런식으로 구현해둘 수 있다.
public class ComicBookFactory implements BookFactory {
@Override
public Book publishBook() {
ComicBook comicBook = new ComicBook();
// 만화책인 경우 팬들에게 알림을 준다고 가정한다. 🤭
this.noticeToFan(comicBook.getTitle());
return comicBook;
}
}
BookFactory 인터페이스
를 상속받아서 만든 ComicBookFactory이다.
public class Library {
private final Book book;
public Library(BookFactory bookFactory) {
this.book = bookFactory.publishBook();
<}
public boolean isOver100() {
return book.getPage() > 100;
}
}
책 공장에서 책을 받아서 어떠한 행동을 할 수 있는 "도서관"이라는 "클라이언트 클래스"를 만들었다.
이제 팩토리 메서드 패턴을 사용할 준비가 완료 됐다.
공장, 공장에서 찍어낼 물건, 그 물건을 사용할 곳이 준비가 된 것이다. 🤭
이제 팩토리 메서드 패턴이 어떻게 쓰이는지 알아보자.
public class MainLogic {
public static void main(String[] args) {
BookFactory bookFactory = new ComicBookFactory();
Library library = new Library(bookFactory);
boolean over100 = library.isOver100();
System.out.println("over100 = " + over100);
}
}
예제 코드 자체는 매우 단순하다.
만화책 공장을 생성해서, Library에 넘겨주고, Library의 메서드를 실행해주는 코드이다.
⭐ 여기서 중요한 것이 2가지가 있다.
ComicBookFactory
의 인스턴스를 BookFactory
인터페이스를 타입으로 갖는 bookFactory
로 할당했다는 것.ComicBookFactory
에서 ComicBook
인스턴스를 만들 때, default메서드인 noticeToFan
이 실행된다는 것.이 2가지가 왜 중요하고, 어떤 장점을 가져다주는 것일까?
위에서 이야기한 2가지 중요한 것은 각각 아래와 같은 의미를 갖는다.
흠... 글로 보니까 뭔가 어려운 것 같은데...
쉽게 이야기 하자면 객체 지향 프로그래밍답게 코딩을 할 수 있다는 것이다 🤭
이는 단순히 "객체 지향의 원칙을 지켰다"라는 의미가 아니고, 코드의 재사용성, 확장성, 안정성등을 아주 잘 보장해준다는 의미가 된다 👍
Spring 프레임워크는 객체 지향 프로그래밍을 극한으로 구현한 프레임워크이다. ㅎㅎ
Spring이 사용하는 개념 중에서 Bean Factory라는 것이 있는데, 이게 바로 팩토리 메서드 패턴을 극한으로 사용한 좋은 예시라고 할 수 있다.