
Factory란 한국어로 공장을 의미하는 만큼, 부모 클래스에서 객체들을 생성할 수 있는 인터페이스를 제공하지만, 자식 클래스들이 생성될 객체들의 유형을 변경할 수 있도록 하는 생성 패턴입니다.
객체를 생성하는 메소드를 추상화하여 서브 클래스에서 실제 객체의 타입을 결정하는 패턴입니다. 이 패턴은 객체 생성과 관련된 코드를 클라이언트 코드와 분리시켜, 클라이언트가 객체의 구체적인 클래스에 의존하지 않도록 합니다. 이렇게 하면 코드의 유지보수성과 확장성이 향상됩니다.
IceCream.java
class IceCream {
private String flavor;
public IceCream(String flavor) {
this.flavor = flavor;
}
public String getFlavor() {
return flavor;
}
public void eat() {
System.out.println(flavor + " 아이스크림을 먹습니다!");
}
}
IceCreamShop.java
public class IceCreamShop {
public IceCream orderIceCream(String flavor) {
IceCream iceCream = createIceCream(flavor);
prepareIceCream(iceCream);
return iceCream;
}
private IceCream createIceCream(String flavor) {
switch (flavor) {
case "초콜릿":
return new ChocolateIceCream();
case "바닐라":
return new VanillaIceCream();
default:
throw new IllegalArgumentException("지원하지 않는 아이스크림 맛입니다: " + flavor);
}
}
private void prepareIceCream(IceCream iceCream) {
System.out.println(iceCream.getFlavor() + " 아이스크림을 준비합니다.");
}
public static void main(String[] args) {
IceCreamShop shop = new IceCreamShop();
IceCream iceCream1 = shop.orderIceCream("초콜릿");
IceCream iceCream2 = shop.orderIceCream("바닐라");
iceCream1.eat(); // 출력: 초콜릿 아이스크림을 먹습니다!
iceCream2.eat(); // 출력: 바닐라 아이스크림을 먹습니다!
}
}
VanillaIceCream.java
class VanillaIceCream extends IceCream {
public VanillaIceCream() {
super("바닐라");
}
}
ChocolateIceCream.java
class ChocolateIceCream extends IceCream {
public ChocolateIceCream() {
super("초콜릿");
}
}
위에 코드는 Factory Method 패턴을 적용하지 않은 프로그램입니다.
이 프로그램은 여러가지 비효율적인 단점이 있습니다.
팩토리 메소드 패턴 적용 전:
IceCreamShop 클래스를 직접 수정해야 합니다. 예를 들어, 새로운 맛의 아이스크림을 추가하려면 해당 메소드를 추가하고 관련 클래스를 수정해야 합니다.IceCreamShop 클래스가 모든 객체 생성 코드를 포함하므로 코드가 복잡해질 수 있고, 유지보수가 어려울 수 있습니다.main 메소드)가 구체적인 클래스에 의존하므로 유연성이 떨어질 수 있습니다. 클라이언트 코드가 바닐라 아이스크림 또는 초콜릿 아이스크림을 생성하는 방법을 알고 있어야 합니다.이러한 단점들을 해결하기 위해 Factory Method 패턴을 활용해 코드를 수정해보았습니다.
Factory.java
package framework;
/**
* 객체를 생성하는 메서드를 정의하는 추상 Factory 클래스입니다.
*/
public abstract class Factory {
/**
* 제품을 생성합니다.
*
* @param owner 제품의 소유자 정보.
* @return 생성된 제품.
*/
public final Product create(String owner) {
Product p = createProduct(owner);
registerProduct(p);
return p;
}
/**
* 구체적인 제품을 생성합니다.
*
* @param owner 제품의 소유자 정보.
* @return 생성된 제품.
*/
protected abstract Product createProduct(String owner);
/**
* 제품을 등록합니다.
*
* @param product 등록할 제품.
*/
protected abstract void registerProduct(Product product);
}
Product.java
package framework;
/**
* 제품에 대한 추상 베이스 클래스입니다.
*/
public abstract class Product {
/**
* 제품을 사용합니다.
*/
public abstract void use();
}
IceCream.java
package icecream;
import framework.Product;
/**
* 아이스크림을 나타내는 구체적인 제품 클래스입니다.
*/
public class IceCream extends Product {
private String flavor;
/**
* 특정 맛의 아이스크림을 생성합니다.
*
* @param flavor 아이스크림의 맛 정보.
*/
public IceCream(String flavor) {
this.flavor = flavor;
}
/**
* 아이스크림을 사용합니다.
*/
@Override
public void use() {
System.out.println(flavor + " 아이스크림을 즐깁니다!");
}
/**
* 아이스크림의 맛을 반환합니다.
*
* @return 아이스크림의 맛 정보.
*/
public String getFlavor() {
return flavor;
}
}
IceCreamFactory.java
package icecream;
import framework.Factory;
import framework.Product;
/**
* 아이스크림 제품을 생성하는 구체적인 팩토리 클래스입니다.
*/
public class IceCreamFactory extends Factory {
/**
* 특정 맛의 아이스크림 제품을 생성합니다.
*
* @param flavor 아이스크림의 맛 정보.
* @return 생성된 아이스크림 제품.
*/
@Override
protected Product createProduct(String flavor) {
return new IceCream(flavor);
}
/**
* 아이스크림 제품을 등록합니다.
*
* @param product 등록할 아이스크림 제품.
*/
@Override
protected void registerProduct(Product product) {
System.out.println(product + "을 아이스크림 데이터베이스에 등록했습니다.");
}
}
Main.java
import framework.Factory;
import framework.Product;
import icecream.IceCreamFactory;
/**
* 아이스크림 팩토리를 사용하는 메인 클래스입니다.
*/
public class Main {
public static void main(String[] args) {
Factory factory = new IceCreamFactory();
Product iceCream1 = factory.create("초콜릿");
Product iceCream2 = factory.create("바닐라");
iceCream1.use(); // 출력: 초콜릿 아이스크림을 즐깁니다!
iceCream2.use(); // 출력: 바닐라 아이스크림을 즐깁니다!
}
}
팩토리 메소드 패턴 적용 후:
createIceCream 메소드를 구현하는 새로운 아이스크림 스토어 클래스를 만들기만 하면 됩니다. 예를 들어, StrawberryIceCreamStore 클래스를 추가하고 createIceCream 메소드를 구현하면 새로운 맛의 아이스크림을 만들 수 있습니다.IceCreamStore 클래스의 orderIceCream 메소드를 호출하면 됩니다. 클라이언트 코드는 구체적인 아이스크림 클래스나 아이스크림 스토어 클래스에 대한 지식이 없어도 됩니다.IceCreamStore를 통해 아이스크림을 주문할 수 있습니다.
UML 사진은 위와 같습니다.
Main 클래스는 프로그램의 진입점을 나타냅니다.Factory 객체를 생성하고 이를 통해 Product를 생성하고 사용합니다.Factory 클래스는 객체 생성을 위한 Creator 클래스입니다.createProduct 메소드를 선언하여 구체적인 팩토리 클래스에서 이를 구현하게 합니다.Product 객체를 생성하고 사용하는 역할을 합니다.Product 클래스는 제품(Product)에 대한 추상 베이스 클래스 또는 인터페이스를 나타냅니다.use 메소드를 선언하여 제품을 사용하는 방법을 정의합니다.IceCream 클래스는 구체적인 제품 클래스입니다.Product 클래스를 상속하며 use 메소드를 구현하여 아이스크림을 사용하는 방법을 정의합니다.IceCreamFactory 클래스는 구체적인 팩토리 클래스입니다.Factory 클래스를 상속하며 createProduct 메소드를 구현하여 특정 맛의 아이스크림 제품을 생성합니다.Factory 클래스를 통해 Product를 생성하고, 구체적인 팩토리 클래스인 IceCreamFactory에서 IceCream 객체를 생성합니다. 클라이언트 코드는 Factory 클래스를 사용하여 제품을 생성하고 사용합니다. Factory Method 패턴을 사용하면 객체 생성과 사용을 분리하여 코드의 확장성과 유지보수성을 향상시킵니다.
Javadoc은 ProxyAfter 프로젝트 docs 폴더에 있습니다. 아래에 파일은 일부입니다.
Factory Method 패턴을 통해 소프트웨어 디자인에서 객체 생성을 추상화하고 유연성을 높이는데 유용하게 코드를 짤 수 있습니다. 또한 코드의 재사용성과 확장 가능성을 증가시킬 수 있으며, 객체 생성과 관련된 복잡성을 숨길 수 있습니다.