CS/디자인패턴

[Design Pattern] Factory Pattern(팩토리 패턴)

2024. 6. 1. 23:47
목차
  1. Factory Parttern이란?
  2. Factory Pattern을 쓰는 이유(==기존 개발 형태에 대한 문제점)?
  3. Factory Pattern(팩토리 패턴) 구현 방법
  4. 1. Simple Factory
  5. 2. Factory Method (추상 메서드 방법)
  6.  3. Abstract Factory (추상 팩토리 방법)
  7. Factory Pattern의 장단점

Factory Parttern이란?

Factory Pattern은 디자인 패턴의 종류 중 생성 디자인 패턴에 속한다.
 
객체 생성 로직을 추상화해서 하위 클래스에서 객체 생성에 대해 구체적인 내용을 결정하는 디자인 패턴이다.


특징: 
객체를 직접 생성하는 대신, 팩토리 클래스를 통해 객체를 생성함으로써 객체 생성 과정에서 발생할 수 있는 의존성을 줄이고, 코드의 유지보수성을 높일 수 있음.

Factory Pattern을 쓰는 이유(==기존 개발 형태에 대한 문제점)?

왜 쓰는지에 대한 관점을 Factory Pattern의 효과 및 장점이 아닌
Factory Pattern을 쓰지 않았던 기존 개발 방식을 사용한다면 어떤 문제점(혹은 불편함)이 발생하는지 예시로 보겠다.
<기존 개발의 문제점>
1. 객체 생성 코드의 중복
2. 코드 확장성 부족
3. 의존성 증가
 
shape라는 인터페이스가 존재하고 해당 인터페이스를 통해 원형 클래스(Circle)과 사각형(Squre) 클래스를 구현한다고 해보자.

// Shape 인터페이스
public interface Shape {
    void draw();
}

// Circle 클래스
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Circle");
    }
}

// Square 클래스
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Square");
    }
}

 

1. 객체 생성 코드의 중복

public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle();
        circle.draw();

        Shape square = new Square();
        square.draw();
    }
}

기존 개발 방식에서 객체를 생성할 때마다 생성로직을 반복하게 되는데 이것은 코드 중복이고 유지보수가 어려워진다.
객체 생성 로직이 여러 곳에 분산되어있으면, 객체 생성 방법이 변경될때마다 모든 곳을 수정해야한다.
 
예를들어 크기가 동일한 원형, 사각형을 만들고 싶어서 생성할때  넓이 값을 변수로 받는 생성 방법으로 변경한다고 생각해보자. 그럼 우리는 생성 로직이 있는 곳마다 변경해야한다.
 

2. 코드 확장성 부족

우리가 유동적으로 Shape 인터페이스로 먼저 생성해놓고 유동적으로 String으로 모양이름을 받은 뒤 생성하는 코드를 만들자고 해보자.
기존 원형과 사각형 클래스 단 2개만 존재할 때 다음과 같을 것이다.

public class Main {
    public static void main(String[] args) {
        Shape shape;

        String shapeType = "CIRCLE"; 
        if (shapeType.equals("CIRCLE")) {
            shape = new Circle();
        } else if (shapeType.equals("SQUARE")) {
            shape = new Square();
        } else {
            shape = null;
        }

        if (shape != null) {
            shape.draw();
        }
    }
}

 
 
여기에 삼각형 클래스를 추가 구현해놨다고 생각해보자.

public class Main {
    public static void main(String[] args) {
        Shape shape;

        String shapeType = "CIRCLE";
        if (shapeType.equals("CIRCLE")) {
            shape = new Circle();
        } else if (shapeType.equals("SQUARE")) {
            shape = new Square();
        } else if (shapeType.equals("TRIANGLE")) {
        	shape = new Triangle();
        } else {
            shape = null;
        }

        if (shape != null) {
            shape.draw();
        }
    }
}

만일 다른 모양들이 생겨나면? 계속 else if 로 추가해야하는 것이다.
이 점은 SOLID 5대 설계 법칙 중 OCP에도 해당되는 데 확장에 전혀 열려있지 않고 확장하려면 계속 코드를 수정 해야한다.
 

3. 의존성 증가

객체를 직접 생성하면 클래스 간의 의존성이 증가한다.
이것은 코드의 결합도를 높이고 SOLID 5대 설계 원칙 중 DIP을 위배한다.

public class DrawingApp {
    private Circle circle;
    private Square square;

    public DrawingApp() {
        this.circle = new Circle();
        this.square = new Square();
    }

    public void drawShapes() {
        circle.draw();
        square.draw();
    }

    public static void main(String[] args) {
        DrawingApp app = new DrawingApp();
        app.drawShapes();
    }
}

너무나 구체적인 클래스인 Circle과 Square을 직접적으로 의존하고있다.
아까처럼 삼각형을 추가하려면? 우리는 DrawingApp 클래스를 수정해야한다.
 
 
Factory Pattern의 효과 및 장점의 관점으로 Factory Pattern을 쓰는 이유는 다음과 같다.
 
<Factory Pattern 사용의 효과>
1. 객체 생성 코드의 캡슐화
2. 코드의 유연성과 확장성
3. 의존성 감소
 

1. 객체 생성 코드의 캡슐화

객체 생성 로직을 한 곳에 모아서 관리할 수 있어. 이를 통해 코드의 가독성과 유지보수성이 향상된다.

2. 코드의 유연성과 확장성

새로운 객체 타입을 추가할 때 기존 코드를 수정하지 않고 확장할 수 있다.

3. 의존성 감소

객체 생성과 관련된 의존성을 줄일 수 있어, 특히 인터페이스를 통해 객체를 생성하면 코드의 결합도가 낮아진다.
 

그럼 아까 봤던 문제되는 상황을 팩토리 패턴으로 수정해보자.

// ShapeFactory 클래스
public class ShapeFactory {
    public Shape createShape(String type) {
        if (type == null) {
            return null;
        }
        if (type.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (type.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }
}

// DrawingApp 클래스
public class DrawingApp {
    private ShapeFactory shapeFactory;

    public DrawingApp(ShapeFactory shapeFactory) {
        this.shapeFactory = shapeFactory;
    }

    public void drawShapes() {
        Shape circle = shapeFactory.createShape("CIRCLE");
        if (circle != null) {
            circle.draw();
        }

        Shape square = shapeFactory.createShape("SQUARE");
        if (square != null) {
            square.draw();
        }
    }

    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();
        DrawingApp app = new DrawingApp(shapeFactory);
        app.drawShapes();
    }
}

 
ShapeFactory 클래스에 캡슐화되어 중복이 제거되고, DrawingApp 클래스는 ShapeFactory를 통해 객체를 생성한다.
Shape를 추가할 때 ShapeFactory 클래스만 수정하면 되고 DrawingApp 클래스는 수정하지 않아도 된다.
DrawingApp 클래스는 ShapeFactory를 통해 객체를 생성하므로, 특정 Shape 클래스에 대한 의존성이 줄어들어 결합도가 낮아진다.
 

Factory Pattern(팩토리 패턴) 구현 방법

1. Simple Factory
2. Factory Method
3. Abstract Factory 

1. Simple Factory

// Shape 인터페이스
public interface Shape {
    void draw();
}

// Circle 클래스
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Circle");
    }
}

// Square 클래스
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Square");
    }
}

// ShapeFactory 클래스
public class ShapeFactory {
    public Shape createShape(String type) {
        if (type == null) {
            return null;
        }
        if (type.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (type.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }
}

// 사용 예시
public class Main {
    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();

        Shape shape1 = shapeFactory.createShape("CIRCLE");
        shape1.draw();

        Shape shape2 = shapeFactory.createShape("SQUARE");
        shape2.draw();
    }
}

 

사실 Simple Factory는 패턴이라기보단 관용구에 가깝다.

단순히 생성하는 부분을 하나의 클래스(Simple Factory)에 모아놓은 것이다.

 

2. Factory Method (추상 메서드 방법)

// Shape 인터페이스
public interface Shape {
    void draw();
}

// Circle 클래스
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Circle");
    }
}

// Square 클래스
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Square");
    }
}

// ShapeFactory 추상 클래스
public abstract class ShapeFactory {
    public abstract Shape createShape();

    public void drawShape() {
        Shape shape = createShape();
        shape.draw();
    }
}

// CircleFactory 클래스
public class CircleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Circle();
    }
}

// SquareFactory 클래스
public class SquareFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Square();
    }
}

// 사용 예시
public class Main {
    public static void main(String[] args) {
        ShapeFactory circleFactory = new CircleFactory();
        circleFactory.drawShape();

        ShapeFactory squareFactory = new SquareFactory();
        squareFactory.drawShape();
    }
}

 

구체적인 클래스의 인스턴스를 생성하기 위해 서브클래스를 통해 팩토리 메서드를 구현하는 방법.


위 코드에서는 CircleFactory와 SquareFactory가 ShapeFactory를 상속받아 createShape 메서드를 구현했다.

 

 3. Abstract Factory (추상 팩토리 방법)

// Shape 인터페이스
public interface Shape {
    void draw();
}

// Circle 클래스
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Circle");
    }
}

// Square 클래스
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Square");
    }
}

public class RoundedRectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Rounded Rectangle");
    }
}
public class RoundedSquare implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Rounded Square");
    }
}

public interface AbstractFactory {
    Shape createShape(String shapeType);
}

public class ShapeFactory implements AbstractFactory {
    @Override
    public Shape createShape(String shapeType) {
        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }
}

public class FactoryProducer {
    public static AbstractFactory getFactory(boolean rounded) {
        if (rounded) {
            return new RoundedShapeFactory();
        } else {
            return new ShapeFactory();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // 일반 Shape 팩토리
        AbstractFactory shapeFactory = FactoryProducer.getFactory(false);

        Shape shape1 = shapeFactory.createShape("CIRCLE");
        shape1.draw(); // Drawing a Circle

        Shape shape2 = shapeFactory.createShape("SQUARE");
        shape2.draw(); // Drawing a Square

        // Rounded Shape 팩토리
        AbstractFactory roundedShapeFactory = FactoryProducer.getFactory(true);

        Shape shape3 = roundedShapeFactory.createShape("RECTANGLE");
        shape3.draw(); // Drawing a Rounded Rectangle

        Shape shape4 = roundedShapeFactory.createShape("SQUARE");
        shape4.draw(); // Drawing a Rounded Square
    }
}

 

관련된 객체 군을 생성하는 팩토리를 제공하는 방법
위 코드에서는 ShapeFactory와 RoundedShapeFactory가 각각 AbstractFactory를 구현하여 여러 객체를 생성한다.
FactoryProducer 클래스가 AbstractFactory 를 구현한 구체적인 팩토리를 생성하는 역할을 한다.

 

Factory Pattern의 장단점

장점

객체 생성 로직의 캡슐화: 객체 생성 로직을 별도의 팩토리 클래스에 캡슐화하여 코드의 가독성과 유지보수성을 향상시킴.
 
확장성: 새로운 객체 타입을 쉽게 추가할 수 있어. 기존 코드를 수정하지 않고 새로운 팩토리 클래스를 추가하면 됨.

의존성 감소: 객체 생성과 관련된 의존성을 줄일 수 있어, 특히 인터페이스를 통해 객체를 생성하면 코드의 결합도가 낮아짐.


단점

클래스의 수 증가: 팩토리 클래스와 객체 클래스가 별도로 존재하므로, 클래스의 수가 증가할 수 있음.
 
복잡성 증가: 객체 생성 로직이 단순한 경우, 팩토리 패턴을 적용하면 오히려 코드의 복잡성이 증가할 수 있음.
 

반응형

'CS > 디자인패턴' 카테고리의 다른 글

[Design Pattern] Proxy Pattern(프록시 패턴)  (0) 2024.06.03
[Design Pattern] Strategy Pattern(전략 패턴)  (0) 2024.06.02
[Design Pattern] Singleton Pattern(싱글톤 패턴)  (0) 2024.05.31
[Design Pattern] Builder Pattern(빌더 패턴)  (1) 2024.02.07
[Design Pattern] Observer pattern(옵저버 패턴)  (0) 2024.01.24
  1. Factory Parttern이란?
  2. Factory Pattern을 쓰는 이유(==기존 개발 형태에 대한 문제점)?
  3. Factory Pattern(팩토리 패턴) 구현 방법
  4. 1. Simple Factory
  5. 2. Factory Method (추상 메서드 방법)
  6.  3. Abstract Factory (추상 팩토리 방법)
  7. Factory Pattern의 장단점
'CS/디자인패턴' 카테고리의 다른 글
  • [Design Pattern] Proxy Pattern(프록시 패턴)
  • [Design Pattern] Strategy Pattern(전략 패턴)
  • [Design Pattern] Singleton Pattern(싱글톤 패턴)
  • [Design Pattern] Builder Pattern(빌더 패턴)
three von
three von
어려워 보이는 프로그래밍 언어를 쉽게 정복하는 블로그
LangEASY : 프로그래밍 언어를 쉽게 정복하는 공간어려워 보이는 프로그래밍 언어를 쉽게 정복하는 블로그
반응형
three von
LangEASY : 프로그래밍 언어를 쉽게 정복하는 공간
three von
전체
오늘
어제
  • 분류 전체보기 (89)
    • BackEnd (5)
    • JAVA (5)
      • 기초개념 (5)
    • 자료구조 & 알고리즘 (7)
      • 기초수학 (0)
      • 선형 자료구조 (4)
      • 비선형 자료구조 (1)
      • 알고리즘 (1)
    • CS (18)
      • 컴퓨터구조 (0)
      • 운영체제 (3)
      • 시스템 소프트웨어 (0)
      • 네트워크 (4)
      • 디자인패턴 (10)
    • 데이터베이스 (4)
    • Spring (4)
    • Project (2)
      • 팀프로젝트 (1)
      • 토이프로젝트 (1)
    • 회고 (0)
    • Git&Github (8)
    • IntelliJ (5)
    • 코테 (16)
      • 프로그래머스 (10)
      • 백준 (6)
    • BookStudy (12)
      • 스프링 부트 핵심 가이드 (12)
    • C++ (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • windowcmd창
  • LiveTemplate사용
  • 윈도우에서 리눅스 명령어
  • 제로베이스백엔드스쿨
  • Java
  • heap 자료구조
  • 자바 선형자료구조
  • 인텔리제이에서 gitbash로 vi vim 에디터 사용하는법
  • spring
  • 개발자
  • vi/vim
  • 깃 이슈관리
  • 자바 자료구조 힙
  • 백엔드스쿨
  • 자바 자바해시맵
  • 제로베이스
  • github이슈관리
  • java heap 자료구조
  • IntelliJ 자동화
  • 제로베이스백엔드스쿨미니과제
  • 백엔드
  • InteliJ에서 gitbash사용
  • githubTest
  • github
  • 코테
  • 명령어변환
  • 백엔드공부
  • vi/vim에디터사용
  • 리눅스 명령어 윈도우 cmd창에서 가능
  • 백엔드 스쿨

최근 댓글

최근 글

hELLO · Designed By 정상우.
three von
[Design Pattern] Factory Pattern(팩토리 패턴)
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.