코딩관계론

디자인 패턴 - 팩토리 패턴 본문

개발/Java

디자인 패턴 - 팩토리 패턴

개발자_티모 2024. 7. 8. 21:47
반응형

팩토리 패턴을 사용하는 이유

팩토리 패턴은 객체 생성 로직을 클라이언트 코드에서 분리하여, 객체 생성의 책임을 클라이언트가 아닌 별도의 팩토리 클래스에서 담당하도록 하는 디자인 패턴입니다. 이를 통해 객체 생성의 세부 사항이 클라이언트 코드에 노출되지 않고, 코드의 유연성과 확장성이 증가하게 됩니다.

 

팩토리 패턴 구현 방법

1. 객체 생성 방법에 대한 Interface를 제공 - Creator Interface

package org.example.factory;

public interface ShipFactory {
    default Ship orderShip(String name, String email){
        prepareOrder(name);
        Ship ship = createShip();
        sendEmailTo(email);
        return ship;
    }
    
    Ship createShip();

    private void prepareOrder(String name){
        System.out.println(name + " 이 주문한 배 접수 완료");
    }

    private void sendEmailTo(String email){
        System.out.println(email +  "이 주문한 배가 다 건조됨");
    }
}

 

2. Creator Interface를 구현하는 ConcreateCreator를 구현

public class WhiteshipFactory implements ShipFactory{
    @Override
    public Ship createShip() {
        return new Whiteship();
    }
}

 

3. Product 정의 및 ConcreateProduct 생성

package org.example.factory;

public class Ship {
    private String name;
    private String color;

    public String getName() {
        return name;
    }

    public void creatOrder(){
        System.out.println("주문 완료");
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}


//다른 파일입니다 
package org.example.factory;

public class Whiteship extends Ship{
    public Whiteship() {
        setName("whiteShip");
        setColor("white");
    }
}

}

 

모든 디자인 패턴은 객체지향원칙을 잘 지키기 위해서 설계된 것이라고 생각됩니다. 만약 우리가 다른 WhiteShip이 아니라 BlackShip을 건조하는 기능을 추가할 때 코드의 수정이 필요할까요? 정답은 아니요입니다.

아래와 같이 코드 수정이 아닌 추가적인 클래들을 생성하면 기능의 확장이 가능합니다. 즉 OCP원칙을 지키게 됩니다. 

package org.example.factory;


public class BlackShipFactory implements ShipFactory{
    @Override
    public Ship createShip() {
        return new Blackship();
    }
}
package org.example.factory;

public class Blackship extends Ship{
    public void Blackship(){
        setName("BlackShip");
        setColor("Black");
    }
}

 

최종 Client는 다음과 같은 방법으로 Factory 패턴을 사용할 수 있게 됩니다.

package org.example;

public class Main {
    public static void main(String[] args) {
        BeanFactory ac = new AnnotationConfigApplicationContext(AppConfig.class);
        
        Ship whiteShip = ac.getBean("whiteShip", Ship.class);
        Ship blackShip = ac.getBean("blackShip", Ship.class);
        
    }
}

//다른 파일입니다 
@Configuration
public class AppConfig {

    @Bean
    public Ship whiteShip(){
        return new WhiteshipFactory().orderShip("white", "twst@naver.com");
    }


    @Bean
    public Ship blackShip(){
        return new WhiteshipFactory().orderShip("black", "twst@naver.com");
    }
}

 

팩토리 패턴의 단점

새로운 기능을 추가하려면 ConcreteCreator와 ConcreteProduct 클래스를 필수적으로 구현해야 합니다. 이러한 문제점을 클래스 폭발이라고 합니다. 

 

클래스 폭발 문제는 메모리와 성능에도 영향이 있게 됩니다. JVM은 많은 수의 클래스를 로드해야 하므로 메모리 사용량이 증가하고, 초기화 시간 및 성능에도 영향을 줄 수 있습니다.

 

스프링에서의 팩토리 패턴은?

스프링 프레임워크에서는 팩토리 패턴을 빈(Bean)을 생성하기 위해 사용합니다. 예를 들어, BeanFactory가 Creator 인터페이스의 역할을 하고, AnnotationConfigApplicationContext가 ConcreteCreator 역할을 합니다. getBean 메서드가 결국 Product(Bean)를 생성하는 역할을 합니다.

public class Main {
    public static void main(String[] args) {
        //client코드 변경이 전혀 없음, 스프링에서 제공해주는 
        // BeanFactory == ShipFactory
        // AnnotationConfigApplicationContext == WhiteShipFactory
        BeanFactory ac = new AnnotationConfigApplicationContext(AppConfig.class);
        
        //SHIP 임
        Ship whiteShip = ac.getBean("whiteShip", Ship.class);
        Ship blackShip = ac.getBean("blackShip", Ship.class);
        
    }
}

 

 

 

 

[참조자료]

백기선님의 자료

https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4/dashboard

 

코딩으로 학습하는 GoF의 디자인 패턴 강의 | 백기선 - 인프런

백기선 | 디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를

www.inflearn.com

 

 

반응형