Static Factory Method

standardChan·2025년 3월 20일
post-thumbnail

Factory Method


개요

RPG 게임 프로젝트에서 Weapon과 WeaponFactory를 구현하는 중에 더 효율적인 Static Factory Method라는 코드를 발견했다.


기존방식 (Concreate Factory Method)

기존에는 Factory 추상클래스를 구현하는 각각의 ConcreteFactory class를 별도로 구현하였다.

사진으로 설명하자면, VehicleDriver 추상클래스를 만들고 이를 구현하는 CarDriver, BusDriver 클래스를 각각 만들어왔다.

이 방식은 새로운 Vehicle을 상속받는 운송수단, Train이 추가되면 VehicleDriver를 상속받는 TrainDriver를 구현하여야한다. 새로운 Class가 추가될때마다 추가적인 Factory Method Class를 만들어야하기때문에 클래스가 많아지면, 관리에 있어 어려움이 있을 수 있다.

public abstract class VehicleFactory {

    public final Weapon createVehicle(String name) {
        return create(name);
    }

    abstract protected Vehicle create(String name);
}
public class CarFactory extends VehicleFactory {

    @Override
    protected Vehicle create(String name) {
        return new Car(name);
    }
}
	VehicleFactory carFactory = new CarFactory();
    Car car = vehicleFactory.createVehicle("sonata");

추상 메서드 VehicleFactory타입으로 선언한 모든 Factory들은 createVehicle메서드 하나로 Vehicle 생성이 가능하다. 이는 OCP을 철저히 준수한다고 볼 수 있다.

OCP는 준수하지만... 의문

CarDriver나 BusDriver나 생성하는 인스턴스만 다르지, 나머지는 똑같은데, 굳이 나눌 필요가 있을까..?

그래서 알아보니, 정적 팩토리 메서드라는 방식이 있다고한다.


Static Factory Method

"Static Method를 통해 간접적으로 생성자를 호출하는 객체를 생성하는 디자인 패턴"


글로는 이해하기 난해한듯 한데, 다시 그림을 통해 이해해보자.

이전에는 CarDriver와 BusDriver라는 팩토리를 별도로 만들어서 생성하였다. 하지만 아래와 같은 방식으로도 생성이 가능하다.

public class VehicleFactory {

    public static Weapon createVehicle(String type, String name) {
    	if (type.equals("car")) {
        	return new Car(name);
        } else if (type.equals("bus")) {
        	return new Bus(name, ATK);
        }
        
        throw new IllegalArgumentException("타입이 존재하지 않습니다.");
    }
}

위와 같이 if-else 문을 이용해서 Car와 Bus를 생성할 수 있다. 하지만 위 코드는 가독성이 떨어지니 다음과 같이 수정하여보자.

public class VehicleFactory {
    private static final Map<String, Function<String, Vegicle>> vehicleMap = new HashMap<>();

    static {
        vehicleMap.put("car", Car::new);
        vehicleMap.put("bus", Bus::new);
    }

    public static Vehicle createVehicle(String type, String name) {
        Function<String, Vehicle> constructor = weaponMap.get("type");
        return constructor.apply(name);
    }
}

훨씬 가독성이 좋아진 걸 볼 수 있다.

새로운 vehicle을 추가할 때에도, vehicleMap에 새로운 Class 생성자만 추가하면 되니 유지보수 또한 쉽다.

또한 Static으로 선언되었기 때문에, 위처럼 별도의 VehicleFactory객체를 선언하는 것 없이 Vehicle을 바로 생성할 수 있다.

마지막으로 String type이라는 매개변수를 통해서 Vehicle type객체를 생성할 수 있으니, 동적으로 생성이 가능하다는 장점이 있다.

Static Factory Method 장점

  1. 코드가 간결하고 유지보수가 쉽다.
  2. 팩토리 객체를 생성하지 않아도 된다.
  3. Map을 활용해 동적 생성 가능하다
    기존 concreateFactory를 각각 생성하는 방식은 동적으로 객체를 생성하는데에 어려움이 있다. 인터페이스/추상메서드의 create를 호출하기 위해서 하위 클래스를 생성해야하기 때문이다.
    하지만 Static Factory Method 방식으로는 매개변수에 생성할 HashKey만 넣으면 되니, 동적으로 생성이 가능하다.

이렇게 생각해보면 대부분의 면에서 Static Factory Method 방식이 유리해보인다. 하지만 해당 클래스의 생성자가 private인 경우, static 방식을 사용할 수 없기 때문에 유의가 필요해보인다.


레퍼런스

Factory Method Image
https://www.startertutorials.com/patterns/factory-method-pattern.html

static Factory Method
https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%95%EC%A0%81-%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%83%9D%EC%84%B1%EC%9E%90-%EB%8C%80%EC%8B%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EC%9E%90

profile
호기심 많은 개발자

0개의 댓글