[Java] Design Pattern: Factory Method Pattern

da-nyee·2020년 8월 23일
0

TIL

목록 보기
4/4
post-thumbnail

이번에는 Design Pattern 중 Factory Method Pattern에 대해 알아보자.

☕ 팩토리 메소드 패턴(Factory Method Pattern)

  • 객체를 만들어내는 부분을 서브 클래스(하위 클래스)에 위임한 패턴이다.
  • 가상 생성자(Virtual Constructor) 라고도 부른다.

😏 장점

  • 하위 클래스에서 생성되는 객체의 타입을 지정할 수 있다.
  • 하위 클래스에서 적절한 객체를 생성하기 때문에 코드 제어에 효과적이다.
  • 상위 클래스에서 직접 객체를 생성하지 않기 때문에 클래스간의 결합도, 의존성이 낮아진다.

😣 단점

  • 재사용의 문제가 있다.
  • 새로운 객체를 사용하고 싶다면 재사용보다는 계속 확장해 나가야 한다.
  • 즉, 자연스레 구현해야 되는 클래스의 개수가 많아진다.

📑 언제 사용될까?

  • 상위 클래스에서 하위 클래스가 무엇을 필요로 하는지 모르는 경우
  • 상위 클래스에서 하위 클래스가 객체를 구체화하여 생성하기를 원하는 경우
  • 상위 클래스에서 하위 클래스가 객체를 생성하도록 고른 경우

🏭 예시

온라인 쇼핑을 예로 들어보자.

  1. 티몬에서 닭가슴살, 비타민을 쇼핑한다.
  2. 쿠팡에서 구운 달걀, 단백질을 쇼핑한다.

쇼핑 과정을 살펴보자.

  1. 쇼핑몰을 정해서 들어간다. 현재 예시에서는 TimonCoupang이다.
  2. 카테고리를 선택한다. 현재 예시에서는 식료품(Food), 건강(Health) 카테고리가 필요하다.
  3. 식료품, 건강 카테고리에서 물건을 찾고(find), 카트에 담고(cart), 주문하고(order), 마지막에 해당 주문을 확인(check) 하면 마무리된다.

코드로 확인해보자.

✅ 예시를 위해 필요한 클래스들을 확인한다.

✅ 쇼핑의 공통과정(find, cart, order, check)인터페이스(interface)에 작성한다.

// Shopping.java

public interface Shopping {
    public void find();  // 물건을 검색한다.
    public void cart();  // 카트에 담는다.
    public void order(); // 카트에 담은 물건을 주문한다.
    public void check(); // 주문이 제대로 되었는지 확인한다.
}

쇼핑몰에서 find, cart, order, check 가 이루어지므로 쇼핑몰을 정의한다.

// ShoppingMall.java

public abstract class ShoppingMall {
    
    public Shopping shopping(String category) {
        Shopping shopping = selectCategory(category); // factory method 사용
    	shopping.find();
    	shopping.cart();
    	shopping.order();
    	shopping.check();
    	return shopping;
    }

    // factory method
    abstract Shopping selectCategory(String category);
}
  • 클래스와 메소드가 추상화(abstract) 되어 있다. 이들은 TimonCoupang에서 재정의(Override)된다.

Timon 클래스를 작성한다.

// Timon.java

public class Timon extends ShoppingMall{

    @Override
    Shopping selectCategory(String category) {

        System.out.println("-----------Timon-----------");

        if (category.equals("Food")) {
            return new TimonFoodCategory();
        } else if (category.equals("Health")) {
            return new TimonHealthCategory();
        }
        return null;
    }
}
  • Timon 클래스는 ShoppingMall 클래스를 상속(extends)받는다.
  • selectCategory 추상 메소드재정의하여 카테고리 객체를 생성한다.
  • category에 따라 TimonFoodCategory, TimonHealthCategory 클래스가 유동적으로 반환된다.

TimonFoodCategory, TimonHealthCategory 클래스를 작성한다.

// TimonFoodCategory.java

public class TimonFoodCategory implements Shopping{
	
    @Override
    public void find() {
        System.out.println("닭가슴살 브랜드를 검색합니다.");
    }
    
    @Override
    public void cart() {
        System.out.println("마음에 드는 닭가슴살을 카트에 담습니다.");
    }
    
    @Override
    public void order() {
        System.out.println("닭가슴살을 주문합니다.");
    }
    
    @Override
    public void check() {
        System.out.println("닭가슴살 주문이 올바르게 되었는지 확인합니다.");
    }
}

// TimonHealthCategory.java

public class TimonHealthCategory implements Shopping{

    @Override
    public void find() {
        System.out.println("구매하려는 비타민을 검색합니다.");
    }
    
    @Override
    public void cart() {
        System.out.println("마음에 드는 비타민을 카트에 담습니다.");
    }
    
    @Override
    public void order() {
        System.out.println("비타민을 주문합니다.");
    }
    
    @Override
    public void check() {
        System.out.println("비타민 주문이 올바르게 되었는지 확인합니다.");
    }
}
  • Shopping 인터페이스에 미리 작성되어 있는 find, cart, order, checkFood, Health 카테고리에서 조금 더 구체적으로 작성된다.

Coupang 클래스를 작성한다.

// Coupang.java

public class Coupang extends ShoppingMall{

    @Override
    Shopping selectCategory(String category) {
    
        System.out.println("-----------Coupang-----------");
        
        if (category.equals("Food")) {
            return new CoupangFoodCategory();
        } else if (category.equals("Health")) {
            return new CoupangHealthCategory();
        }
        return null;
    }
}

CoupangFoodCategory, CoupangHealthCategory 클래스를 작성한다.

// CoupangFoodCategory.java

public class CoupangFoodCategory implements Shopping{

    @Override
    public void find() {
        System.out.println("구운 달걀을 검색합니다.");
    }
    
    @Override
    public void cart() {
        System.out.println("참나무로 구운(...) 달걀을 카트에 담습니다.");
    }
    
    @Override
    public void order() {
        System.out.println("달걀을 주문합니다.");
    }
    
    @Override
    public void check() {
        System.out.println("달걀 주문이 올바르게 되었는지 확인합니다.");
    }
}

// CoupangHealthCategory.java

public class CoupangHealthCategory implements Shopping{

    @Override
    public void find() {
        System.out.println("구매하려는 프로틴을 검색합니다.");
    }
    
    @Override
    public void cart() {
        System.out.println("마음에 드는 프로틴을 카트에 담습니다.");
    }
    
    @Override
    public void order() {
        System.out.println("프로틴을 주문합니다.");
    }
    
    @Override
    public void check() {
        System.out.println("프로틴 주문이 올바르게 되었는지 확인합니다.");
    }
}

✅ Main 클래스에서 어떻게 동작하는지 확인한다.

// Main.java

public class Main {

    public static void main(String[] args) {
    
        // Timon 쇼핑
        ShoppingMall timon = new Timon();
        
        Shopping chickenBreast = timon.shopping("Food"); // 식료품 카테고리
        Shopping vitamin = timon.shopping("Health");     // 건강 카테고리
        
        // Coupang 쇼핑
        ShoppingMall coupang = new Coupang();
        
        Shopping roastedEgg = coupang.shopping("Food");  // 식료품 카테고리
        Shopping protein = coupang.shopping("Health");   // 건강 카테고리
    
    }
}
  • Timon, Coupang 클래스를 불러온다.
  • 해당 클래스들은 ShoppingMall 클래스를 상속받았다.
  • 해당 클래스들에서 selectCategory 추상 메소드find, cart, order, check가 동작한다.
  • selectCategory에 알맞은 category를 넣어주면, 매개변수에 따라 맞는 카테고리 객체가 반환된다.

✅ Main 클래스의 실행 결과를 확인한다.

  • 카테고리 객체가 유동적으로 반환되어 실행되는 것을 볼 수 있다.
  • 이렇듯, 팩토리 메소드 패턴은 결합도를 낮추면서 확장을 계속해 나갈 수 있다.
  • 그러나, 재사용의 문제가 있으므로 유념해서 사용해야 한다.


🔎 참고자료

profile
매일매일을 소중하게 ✨

0개의 댓글