상세 컨텐츠

본문 제목

[Design Pattern] 팩토리 메서드 패턴(Factory Method Pattern)

Development/Java

by thisisnew 2019. 10. 21. 20:10

본문

반응형

 

이번에는 디자인 패턴 중에 하나인 '팩토리 메서드 패턴'에 대해 알아보도록 하겠습니다.

 

이는 하위 클래스에서 객체를 만들어주는 것을 '공장(Factory)'에 비유한 것인데요.

 

이 패턴은 객체의 생성을 하위 클래스에서 대신합니다.

 

인터페이스를 사용하여, 하위 클래스에서 줄기가 갈라지며 적절한 객체를 생성하기 때문에 코드 제어에 효과적입니다.

 

더불어 상위 클래스에서 직접 객체를 생성하지 않으므로 의존성도 낮아지죠.

 

이해하기 쉬운 상황과, 그 상황을 코드로 구현해봤는데요.

 

차례대로 살펴보겠습니다.

 


인터넷 쇼핑을 예로 들어보죠.

 

건강을 위해 ㅌ몬에서 비타민과 닭가슴살을, ㅋ팡에서 프로틴(유청단백질)과 구운 달걀을 쇼핑하는 과정을 가정해 보도록 하겠습니다.

 

  1. 먼저, 쇼핑몰을 정해서 들어가겠죠? Timon(...)과 Koupang(...)입니다.
  2. 다음은 카테고리를 선택해야 합니다. 여기서는 건강(Health) 카테고리와 식료품(Food) 카테고리겠네요.
  3. 해당 카테고리에서 물건을 찾고(find), 카트에 담고(cart), 주문하고(order), 마지막으로 해당 주문을 확인(check)하면 마무리가 됩니다.

이 과정을 염두에 두고 코드를 봐주시면 되겠습니다.

 

필요한 클래스는 다음과 같습니다.

 

Main 클래스는 실행을 위한 것이고, 나머지는 전부 구현부입니다.

 

먼저, 쇼핑할 때 구매하려는 물건을 찾고(find), 카트에 담고(cart), 주문하고(order), 주문을 확인하는(check) 것은 공통 과정이죠?

 

이 공통 과정은 인터페이스에 작성하도록 하겠습니다.

//Shopping.java

public interface Shopping {
    public void find(); //물건을 검색한다
    public void cart(); //카트에 담는다.
    public void order(); //카트에 담은 물건을 주문한다.
    public void 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) 되어있죠?

 

이것들은 Timon과 Koupang에서 구체화, 재정의 될 것입니다.

 

이제 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) 받습니다.

 

이는 나중에 Main 클래스에서 선언을 해주면 ShoppingMall 클래스에 기작성 되어있던 selectCategory 메서드와 쇼핑 과정들(find, cart, order, check)이 동작하는 것을 볼 수 있습니다.

 

또한 여기서 추상 메서드인 selectCategory를 재정의(Override)하여 카테고리 객체를 생성해주는 것을 볼 수 있는데요.

 

문자열(category)에 따라 식료품 카테고리(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 인터페이스에 공통 과정들을 미리 작성해뒀던 것 기억하시나요?

 

식료품 카테고리와 건강 카테고리에서 그 과정들을 좀 더 구체적으로 작성해주었습니다.

 

그럼 Koupang도 Timon과 동일한 방식으로 작성하도록 하겠습니다.

//Koupang.java

public class Koupang extends ShoppingMall{

  @Override
  Shopping selectCategory(String category) {
  
  System.out.println("-----------Koupang-----------");
  
  if (category.equals("Food")) {
  	return new KoupangFoodCategory();
  } else if (category.equals("Health")) {
  	return new KoupangHealthCategory();
  }
  	return null;
  }
    
}
//KoupangFoodCategory.java

public class KoupangFoodCategory 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("달걀 주문이 올바르게 되었는지 확인합니다.");
  }
  
}
//KoupangHealthCategory.java

public class KoupangHealthCategory 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 vitamin = timon.shopping("Health"); //건강 카테고리
    Shopping chickenBreast = timon.shopping("Food"); // 식료품 카테고리


    //Koupang 쇼핑
    ShoppingMall koupang = new Koupang();

    Shopping protein = koupang.shopping("Health"); //건강 카테고리
    Shopping roastedEgg = koupang.shopping("Food"); //식료품 카테고리
    
  }
}

먼저 Timon 클래스를 불러옵니다.

 

Timon 클래스는 ShoppingMall 클래스를 상속받았으므로, 위에서 말씀드렸듯이 기작성 된 selectCategory(메서드)와 4개의 과정들이 동작하게 됩니다.

 

그중 가장 먼저 동작하는 selectCategory에 알맞은 매게 변수(문자열 category)를 넣어주면, 그에 따라 적절한 카테고리 객체가 반환되는 것이죠.

 

Koupang도 동일합니다.

 

이제 실행해 보도록 하겠습니다.

 

카테고리 객체가 유동적으로 반환되어 실행되는 것을 볼 수 있죠?

 

이렇듯 팩토리 메서드 패턴은 결합도를 낮추면서 확장을 계속해 나갈 수 있는데요.

 

단, 단점이 있습니다.

 

바로 재사용의 문제인데요.

 

새로운 객체를 사용하고 싶다면 재사용보단 계속해서 확장해야 할 소지가 있습니다.  

 

이건 즉, 자연스레 구현해야 하는 클래스의 개수가 많아짐을 의미하게 되죠. 

 

이 점을 유념하시기 바랍니다.

 

반응형

관련글 더보기

댓글 영역