첫 디자인 패턴에 대한 설명으로 팩토리 패턴에 대해서 설명하겠다!
팩토리 패턴이란?
팩토리 패턴은 객체 생성에 관련된 디자인 패턴으로, 객체 생성의 책임 클라이언트 코드에서 분리하여 코드의 유연성과 확장성을 높이는 디자인 패턴이다!
아래에 간단한 샘플 예재를 봐보자.
public interface Car {
void drive();
}
public class Sedan implements Car {
@Override
public void drive() {
System.out.println("Sedan is driving");
}
}
public class SUV implements Car {
@Override
public void drive() {
System.out.println("SUV is driving");
}
}
public class Client {
public static void main(String[] args) {
Car sedan = new Sedan();
Car suv = new SUV();
sedan.drive();
suv.drive();
}
}
위와 같은 코드의 문제점은 무엇인가?
- 객체 생성의 결합도
Client 클래스가 Sedan과 SUV 인스턴스를 직접 생성하고 있다. 이는 인스턴스 생성 코드가 여러 곳에 분산될 수 있고, 새로운 Car 타입이 추가될 때마다 Client 클래스를 수정해야하는 문제를 야기한다!
- 확장성 부족
새로운 Car 타입을 추가할 때마다 Client 클래스의 코드를 수정해야 하기 때문에, 유지보수성이 떨어질 수 있다!
- 캡슐화 부족
객체 생성 로직이 클라이언트 코드에 노출되어 있다! 이는 클라이언트 코드와 구체적인 클래스들 간의 결합도를 높일 수 있다!
이럴때 사용하는것이 팩토리 패턴이다.
팩토리 패턴은 객체의 생성을 캡슐화하여, 비지니스 코드와의 결합도(의존성)을 낮추어 코드를 건드리는 횟수를 최소화할 수 있게 도와주는 디자인 패턴이다.
1. 심플 팩토리 패턴 (Simple Factory Pattern)
심플 팩토리 패턴은 간단하게 말해서 객체를 생성하는 클래스를 따로 두는 것을 말한다.
위 코드의 심플 팩토리 패턴을 적용시키면 아래와 같이 코드를 짤 수 있다.
public class CarFactory {
public static Car createCar(String type) {
if (type.equalsIgnoreCase("Sedan")) {
return new Sedan();
} else if (type.equalsIgnoreCase("SUV")) {
return new SUV();
}
throw new IllegalArgumentException("Unknown car type: " + type);
}
}
public class Client {
public static void main(String[] args) {
Car sedan = CarFactory.createCar("Sedan");
Car suv = CarFactory.createCar("SUV");
sedan.drive();
suv.drive();
}
}
이렇게 심플 팩토리 패턴을 사용하여, 객체를 생성하는 클래스를 외부에 두어, 클라이언트 코드를 단순화 하고, 확장성을 향상할 수 있다!
다만 이 코드에서도 문제점은 있다!
현재 팩토리 클래스를 보면 type을 결정 짓는 책임과, type을 기반으로 어떤 객체를 생성할지 결정하는 책임 즉, 2개의 책임을 모두 지고 있다. 이는 단일 책임 원칙에 위배된다!
2. 팩토리 메소드 패턴(Factory Method Pattern)
이와같은 상황에서 만약 Car타입이 자주 추가되지는 않지만, 각 Car 타입의 생성 로직을 분리하고 싶다면 팩토리 메소드 패턴을 사용하면 된다.
public interface CarFactory {
Car createCar();
}
public class SedanFactory implements CarFactory {
@Override
public Car createCar() {
return new Sedan();
}
}
public class SUVFactory implements CarFactory {
@Override
public Car createCar() {
return new SUV();
}
}
public class Client {
public static void main(String[] args) {
CarFactory sedanFactory = new SedanFactory();
CarFactory suvFactory = new SUVFactory();
Car sedan = sedanFactory.createCar();
Car suv = suvFactory.createCar();
sedan.drive();
suv.drive();
}
}
이렇게 팩토리 메소드 패턴을 사용하여, 객체 생성의 책임을 서브클래스로 위임하여, OCP를 준수하고, 각 클래스가 자신이 생성해야 할 객체를 직접 관리할 수 있다!
추후에, 자동차의 종류가 늘어난다 하여도, Car 구현체 클래스의 생성과 createCar()의 분기처리 해줌으로써 확장이 가한 구조가 된다!
3. 추상 팩토리 패턴(Abstract Factory Pattern)
만약 Car 타입과 함께 객체들도 함께 생성해야 한다면(예: 엔진, 타이어 등), 추상 팩토리 패턴을 적용하면 된다!
public interface Car {
void drive();
}
public interface Engine {
void start();
}
public class Sedan implements Car {
@Override
public void drive() {
System.out.println("Sedan is driving");
}
}
public class SedanEngine implements Engine {
@Override
public void start() {
System.out.println("Sedan engine is starting");
}
}
public class SUV implements Car {
@Override
public void drive() {
System.out.println("SUV is driving");
}
}
public class SUVEngine implements Engine {
@Override
public void start() {
System.out.println("SUV engine is starting");
}
}
public interface CarFactory {
Car createCar();
Engine createEngine();
}
public class SedanFactory implements CarFactory {
@Override
public Car createCar() {
return new Sedan();
}
@Override
public Engine createEngine() {
return new SedanEngine();
}
}
public class SUVFactory implements CarFactory {
@Override
public Car createCar() {
return new SUV();
}
@Override
public Engine createEngine() {
return new SUVEngine();
}
}
public class Client {
private Car car;
private Engine engine;
public Client(CarFactory factory) {
car = factory.createCar();
engine = factory.createEngine();
}
public void testDrive() {
car.drive();
engine.start();
}
public static void main(String[] args) {
CarFactory sedanFactory = new SedanFactory();
Client client1 = new Client(sedanFactory);
client1.testDrive();
CarFactory suvFactory = new SUVFactory();
Client client2 = new Client(suvFactory);
client2.testDrive();
}
}
이렇게 추상 팩토리 패턴을 사용함으로써 얻는 이점은,
- 구체적인 클래스를 사용자로부터 분리할 수 있다!
사용자는 정의된 인터페이스에 정의된 추상 메소드만을 사용하면 된다.
- 제품군을 쉽게 대체할 수 있다!
만약, SUV를 Truck으로 생성하고 싶다면, Truck을 구현 후, TruckFactory만 구현 해주면 된다.
예재(Github)
https://github.com/SeolSongWoo/design-pattern-in-java/tree/master/src/main/java/com/suseol/factory
'개발 > 디자인 패턴' 카테고리의 다른 글
디자인 패턴에 대한 6가지 힌트 (0) | 2024.05.21 |
---|