S E P H ' S

[생성 패턴] 팩토리(Factory) 패턴 본문

Programing & Coding/Design Pattern

[생성 패턴] 팩토리(Factory) 패턴

yoseph0310 2023. 3. 4. 21:13

팩토리(Factory) 패턴

팩토리 패턴은 객체를 생성하는 인터페이스는 미리 정의하되 인스턴스를 만들 클래스의 결정은 서브클래스에서 내리는 패턴이다. 다시 말해 여러 개의 서브 클래스를 가진 슈퍼 클래스가 있을 때 인풋에 따라 하나의 자식 클래스의 인스턴스를 리턴해주는 방식이다.

팩토리 패턴에서는 클래스의 인스턴스를 만드는 시점을 서브 클래스로 미룬다.

이 패턴은 인스턴스화에 대한 책임을 객체를 사용하는 클라이언트에서 팩토리 클래스로 가져온다.

 

언제 사용하는가?

  1. 어떤 클래스가 자신이 생성해야 하는 객체의 클래스를 예측할 수 없을 때
  2. 생성할 객체를 기술하는 책임을 자신의 서브 클래스가 지정했으면 할 때

팩토리 패턴에 사용되는 슈퍼 클래스는 인터페이스나 추상 클래스, 혹은 평범한 자바 클래스여도 상관없다.

 

Super Class

public abstract class Computer {
	
    public abstract String getRAM();
    public abstract String getHDD();
    public abstract String getCPU();
    
    @Override
    public String toString() {
    	return "RAM= "+this.getRAM()+", HDD="+this.getHDD()+", CPU="+this.getCPU();
    }
}

 

Sub Class - 1

 

public class PC extends Computer {
    
    private String ram;
    private String hdd;
    private String cpu;

    public PC(String ram, String hdd, String cpu) {
        this.ram = ram;
        this.hdd = hdd;
        this.cpu = cpu;
    }

    @Override
    public String getRAM() {
        return this.ram;
    }

    @Override
    public String getHDD() {
        return this.hdd;
    }

    @Override
    public String getCPU() {
        return this.cpu;
    }
}

 

Sub Class - 2

 

public class Server extends Computer {

    private String ram;
    private String hdd;
    private String cpu;

    public Server(String ram, String hdd, String cpu) {
        this.ram = ram;
        this.hdd = hdd;
        this.cpu = cpu;
    }

    @Override
    public String getRAM() {
        return this.ram;
    }

    @Override
    public String getHDD() {
        return this.hdd;
    }

    @Override
    public String getCPU() {
        return this.cpu;
    }
}

 

PC 클래스와 Server 클래스 모두 Computer 클래스를 상속했다.

 

Factory Class

 

public class ComputerFactory {

    public static Computer getComputer(String type, String ram, String hdd, String cpu) {
        if ("PC".equalsIgnoreCase(type)) return new PC(ram, hdd, cpu);
        else if ("Server".equalsIgnoreCase(type)) return new Server(ram, hdd, cpu);

        return null;
    }
}

 

ComputerFactory 클래스의 getComputer 메소드를 보면 static 메소드로 구현되었고, 메소드 내부를 보면 type의 값이 "PC"일 경우 PC의 인스턴스를 "Server"일 경우 Server의 인스턴스를 리턴하는 것을 볼 수 있다.

 

이렇게 팩토리 메소드 패턴을 사용하면 인스턴스를 필요로 하는 Application에서 Computer의 서브 클래스에 대한 정보는 모른채 인스턴스를 생성할 수 있게 된다.

이렇게 구현한다면 Computer 클래스에 더 많은 Sub 클래스가 추가된다 할지라도 getComputer()를 통해 인스턴스를 제공받던 Application의 코드는 수정할 필요가 없게 된다.

 

팩토리 메소드 패턴을 구현하는데 중요한 두 가지가 있다.

 

  1. Factory class를 Singleton으로 구현해도 되고, 서브클래스를 리턴하는 static 메소드로 구현해도 된다.
  2. 팩토리 메소드는 위 예제의 getComputer()와 같이 입력된 파라미터에 따라 다른 서브 클래스의 인스턴스를 생성하고 리턴한다.

마지막으로, 위 예제에서 작성한 ComputerFactory 클래스를 사용해 PC와 Server의 인스턴스를 생성해보자.

 

public class TestFactory {
    public static void main(String[] args) {
        Computer pc = ComputerFactory.getComputer("pc", "2 GB", "500 GB", "2.4 GHz");
        Computer server = ComputerFactory.getComputer("server", "16 GB", "1 TB", "2.9 GHz");

        System.out.println("Factory PC : " + pc);
        System.out.println("Factory Server : " + server);

    }
}

 

 

팩토리 패턴의 장점

  1. 팩토리 패턴은 클라이언트 코드로부터 서브 클래스의 인스턴스화를 제거하여 서로 간의 종속성을 낮추고, 결합도를 느슨하게 하며 (Loosely Coupled), 확장을 쉽게한다.
    예를 들어, 위 예제에서 작성한 클래스 중 PC class에 대해 수정, 삭제가 일어나더라도 클라이언트는 알 수 없기 때문에 코드를 변경할 필요가 없다.
  2. 팩토리 패턴은 클라이언트와 구현 객체들 사이에 추상화를 제공한다.

 

사용 예

  1. java.util 패키지에 있는 Calendar, ResourceBundle, NumberFormat 등의 클래스에서 정의된 getInstance() 가 팩토리 패턴을 사용하고 있다.
  2. Boolean, Integer, Long 등 Wrapper Class 안에 정의된 valueOf() 메소드 또한 팩토리 패턴을 사용했다.

 

출처 : https://readystory.tistory.com/117