ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 팩토리 패턴(Factory Pattern)이란?
    Design Pattern/생성 디자인 패턴 2024. 3. 31. 18:16
    반응형

    1. 팩토리 패턴이란?

     팩토리 패턴(Factory Pattern)은 객체 생성을 위한 인터페이스를 제공하며, 서브클래스가 인스턴스화할 클래스를 결정하게 합니다. 이를 통해 객체 생성을 캡슐화하고, 클라이언트 코드가 특정 클래스에 의존하지 않도록 도와줍니다. 이 패턴은 객체 생성 로직과 클라이언트 코드를 분리하여, 코드의 유연성과 확장성을 향상시킵니다.

    2. 팩토리 패턴의 종류

    팩토리 패턴에는 주로 세 가지 종류가 있습니다:

    a. 단순 팩터리 패턴 (Simple Factory Pattern)

     단순 팩터리 패턴은 팩토리 클래스가 객체 생성의 전체 로직을 처리합니다. 클라이언트는 팩토리 클래스를 통해 필요한 객체를 요청합니다.

     

    Java 예시:

    public class ShapeFactory {
        public Shape getShape(String shapeType){
            if(shapeType == null){
                return null;
            }        
            if(shapeType.equalsIgnoreCase("CIRCLE")){
                return new Circle();
            } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
                return new Rectangle();
            }        
            return null;
        }
    }

     

    Go 예시:

    package main
    
    import "fmt"
    
    type Shape interface {
        Draw()
    }
    
    type Circle struct{}
    func (c Circle) Draw() {
        fmt.Println("Inside Circle::draw() method.")
    }
    
    type Rectangle struct{}
    func (r Rectangle) Draw() {
        fmt.Println("Inside Rectangle::draw() method.")
    }
    
    func getShape(shapeType string) Shape {
        switch shapeType {
        case "circle":
            return Circle{}
        case "rectangle":
            return Rectangle{}
        default:
            return nil
        }
    }
    
    func main() {
        shape1 := getShape("circle")
        shape1.Draw()
    }

     

     

    b. 팩터리 메서드 패턴 (Factory Method Pattern)

     팩터리 메서드 패턴에서는 객체 생성을 서브클래스에게 위임합니다. 팩토리 메서드는 인터페이스를 통해 객체를 생성하며, 어떤 클래스의 인스턴스를 생성할지는 서브클래스가 결정합니다.

     

    Java 예시:

    public interface Shape {
        void draw();
    }
    
    public class Circle implements Shape {
        @Override
        public void draw() {
            System.out.println("Inside Circle::draw() method.");
        }
    }
    
    public abstract class ShapeFactory {
        abstract Shape getShape();
    }
    
    public class CircleFactory extends ShapeFactory {
        @Override
        Shape getShape() {
            return new Circle();
        }
    }

     

     

    Go 예시:

    package main
    
    import "fmt"
    
    type Shape interface {
        Draw()
    }
    
    type Circle struct{}
    
    func (c Circle) Draw() {
        fmt.Println("Inside Circle::draw() method.")
    }
    
    type ShapeFactory interface {
        GetShape() Shape
    }
    
    type CircleFactory struct{}
    
    func (cf CircleFactory) GetShape() Shape {
        return Circle{}
    }
    
    func main() {
        circleFactory := CircleFactory{}
        shape := circleFactory.GetShape()
        shape.Draw()
    }

     

     

    c. 추상 팩터리 패턴 (Abstract Factory Pattern)

     추상 팩터리 패턴은 관련된 객체의 패밀리를 생성하기 위한 인터페이스를 제공합니다. 이 패턴을 사용하면 구체적인 클래스를 지정하지 않고도 서로 관련된 여러 객체를 생성할 수 있습니다.

     

    Java 예시:

    // Shape 인터페이스와 구현체
    public interface Shape {
        void draw();
    }
    
    class Circle implements Shape {
        @Override
        public void draw() {
            System.out.println("Inside Circle::draw() method.");
        }
    }
    
    class Square implements Shape {
        @Override
        public void draw() {
            System.out.println("Inside Square::draw() method.");
        }
    }
    
    // Color 인터페이스와 구현체
    public interface Color {
        void fill();
    }
    
    class Red implements Color {
        @Override
        public void fill() {
            System.out.println("Inside Red::fill() method.");
        }
    }
    
    class Green implements Color {
        @Override
        public void fill() {
            System.out.println("Inside Green::fill() method.");
        }
    }
    
    // AbstractFactory 인터페이스
    public abstract class AbstractFactory {
        abstract Shape getShape(String shapeType);
        abstract Color getColor(String colorType);
    }
    
    // ConcreteFactory 클래스들
    class ShapeFactory extends AbstractFactory {
        @Override
        Shape getShape(String shapeType) {
            if(shapeType == null){
                return null;
            }
            if(shapeType.equalsIgnoreCase("CIRCLE")){
                return new Circle();
            } else if(shapeType.equalsIgnoreCase("SQUARE")){
                return new Square();
            }
            return null;
        }
    
        @Override
        Color getColor(String colorType) {
            // ShapeFactory는 Color 객체를 생성할 책임이 없음
            return null;
        }
    }
    
    class ColorFactory extends AbstractFactory {
        @Override
        Shape getShape(String shapeType) {
            // ColorFactory는 Shape 객체를 생성할 책임이 없음
            return null;
        }
    
        @Override
        Color getColor(String colorType) {
            if(colorType == null){
                return null;
            }
            if(colorType.equalsIgnoreCase("RED")){
                return new Red();
            } else if(colorType.equalsIgnoreCase("GREEN")){
                return new Green();
            }
            return null;
        }
    }
    
    // FactoryProducer 클래스
    class FactoryProducer {
        public static AbstractFactory getFactory(boolean rounded) {
            if(rounded){
                return new ShapeFactory();
            } else {
                return new ColorFactory();
            }
        }
    }
    
    // 클라이언트 코드
    public class AbstractFactoryPatternDemo {
        public static void main(String[] args) {
            // Shape 팩토리 가져오기
            AbstractFactory shapeFactory = FactoryProducer.getFactory(true);
    
            // Shape 객체 가져오기
            Shape shape1 = shapeFactory.getShape("CIRCLE");
            shape1.draw();
    
            // Color 팩토리 가져오기
            AbstractFactory colorFactory = FactoryProducer.getFactory(false);
    
            // Color 객체 가져오기
            Color color1 = colorFactory.getColor("RED");
            color1.fill();
        }
    }

     

    Go 예시:

    package main
    
    import "fmt"
    
    // Shape 인터페이스
    type Shape interface {
        Draw()
    }
    
    // Color 인터페이스
    type Color interface {
        Fill()
    }
    
    // Shape 인터페이스를 구현하는 Concrete 구조체
    type Circle struct{}
    
    func (c Circle) Draw() {
        fmt.Println("Inside Circle::Draw() method.")
    }
    
    type Square struct{}
    
    func (s Square) Draw() {
        fmt.Println("Inside Square::Draw() method.")
    }
    
    // Color 인터페이스를 구현하는 Concrete 구조체
    type Red struct{}
    
    func (r Red) Fill() {
        fmt.Println("Inside Red::Fill() method.")
    }
    
    type Green struct{}
    
    func (g Green) Fill() {
        fmt.Println("Inside Green::Fill() method.")
    }
    
    // AbstractFactory 인터페이스
    type AbstractFactory interface {
        GetShape(shapeType string) Shape
        GetColor(colorType string) Color
    }
    
    // ShapeFactory 구조체와 메서드
    type ShapeFactory struct{}
    
    func (sf ShapeFactory) GetShape(shapeType string) Shape {
        switch shapeType {
        case "circle":
            return Circle{}
        case "square":
            return Square{}
        default:
            return nil
        }
    }
    
    func (sf ShapeFactory) GetColor(colorType string) Color {
        // ShapeFactory는 Color 객체를 생성할 책임이 없으므로 nil 반환
        return nil
    }
    
    // ColorFactory 구조체와 메서드
    type ColorFactory struct{}
    
    func (cf ColorFactory) GetShape(shapeType string) Shape {
        // ColorFactory는 Shape 객체를 생성할 책임이 없으므로 nil 반환
        return nil
    }
    
    func (cf ColorFactory) GetColor(colorType string) Color {
        switch colorType {
        case "red":
            return Red{}
        case "green":
            return Green{}
        default:
            return nil
        }
    }
    
    // FactoryProducer: 팩토리 타입에 따라 ShapeFactory 또는 ColorFactory 인스턴스 반환
    func GetFactory(factoryType string) AbstractFactory {
        if factoryType == "shape" {
            return ShapeFactory{}
        } else if factoryType == "color" {
            return ColorFactory{}
        }
        return nil
    }
    
    func main() {
        shapeFactory := GetFactory("shape")
        shape1 := shapeFactory.GetShape("circle")
        shape1.Draw()
    
        colorFactory := GetFactory("color")
        color1 := colorFactory.GetColor("red")
        color1.Fill()
    }

     

     

    3. Java와 Go의 차이점

     Java는 객체 지향 프로그래밍 언어로, 클래스와 인터페이스를 통한 상속과 다형성을 자연스럽게 지원합니다. 반면, Go는 인터페이스를 통한 다형성은 지원하지만, 전통적인 클래스 기반 상속 대신 구조체와 구성(composition)을 사용합니다. 이러한 차이는 팩토리 패턴을 구현할 때 Java는 클래스 상속과 오버라이딩을, Go는 인터페이스와 구조체를 사용하여 다형성과 객체 생성 책임의 분리를 달성합니다.

     

    4. 팩토리 패턴의 장단점

    장점

    • 객체 생성과 클래스 구현을 분리: 클라이언트 코드가 객체 생성 과정에 의존하지 않게 함으로써, 코드의 유연성과 재사용성을 향상시킵니다.
    • 확장성: 새로운 클래스를 추가하거나 변경할 때 클라이언트 코드를 변경하지 않고도 대응할 수 있습니다.
    • 코드 유지보수성 향상: 객체 생성 코드가 한 곳에 집중되어 있어, 코드 관리가 용이합니다.

    단점

    • 코드 복잡성 증가: 많은 클래스와 인터페이스가 도입되어 전체적인 시스템 구조가 복잡해질 수 있습니다.
    • 런타임 오버헤드: 간단한 객체 생성보다 더 많은 리소스와 시간이 소요될 수 있습니다.

     

     

    5. 고급 팩토리 패턴 예시

    고급 예시로, 추상 팩토리 패턴을 사용하여 GUI 라이브러리를 구현하는 상황을 들어보겠습니다. 이 라이브러리는 여러 운영체제(예: Windows, MacOS)에서 사용될 수 있으며, 각 운영체제에 맞는 버튼, 체크박스 등의 컴포넌트를 생성합니다.

    Java 예시: GUI 라이브러리

    // Component interfaces
    interface Button {
        void paint();
    }
    
    interface Checkbox {
        void paint();
    }
    
    // Concrete components for Windows
    class WindowsButton implements Button {
        public void paint() {
            System.out.println("Rendering a button in a Windows style.");
        }
    }
    
    class WindowsCheckbox implements Checkbox {
        public void paint() {
            System.out.println("Rendering a checkbox in a Windows style.");
        }
    }
    
    // Concrete components for MacOS
    class MacOSButton implements Button {
        public void paint() {
            System.out.println("Rendering a button in a MacOS style.");
        }
    }
    
    class MacOSCheckbox implements Checkbox {
        public void paint() {
            System.out.println("Rendering a checkbox in a MacOS style.");
        }
    }
    
    // Abstract factory
    interface GUIFactory {
        Button createButton();
        Checkbox createCheckbox();
    }
    
    // Concrete factories
    class WindowsFactory implements GUIFactory {
        public Button createButton() {
            return new WindowsButton();
        }
        public Checkbox createCheckbox() {
            return new WindowsCheckbox();
        }
    }
    
    class MacOSFactory implements GUIFactory {
        public Button createButton() {
            return new MacOSButton();
        }
        public Checkbox createCheckbox() {
            return new MacOSCheckbox();
        }
    }
    
    // Client code
    class Application {
        private Button button;
        private Checkbox checkbox;
    
        public Application(GUIFactory factory) {
            button = factory.createButton();
            checkbox = factory.createCheckbox();
        }
    
        public void paint() {
            button.paint();
            checkbox.paint();
        }
    }
    
    public class Demo {
        public static void main(String[] args) {
            Application app = new Application(new WindowsFactory());
            app.paint();
        }
    }

     

     

    Go 예시: GUI 라이브러리

    package main
    
    import "fmt"
    
    // Component interfaces
    type Button interface {
        Paint()
    }
    
    type Checkbox interface {
        Paint()
    }
    
    // Concrete components for Windows
    type WindowsButton struct{}
    
    func (b WindowsButton) Paint() {
        fmt.Println("Rendering a button in a Windows style.")
    }
    
    type WindowsCheckbox struct{}
    
    func (c WindowsCheckbox) Paint() {
        fmt.Println("Rendering a checkbox in a Windows style.")
    }
    
    // Concrete components for MacOS
    type MacOSButton struct{}
    
    func (b MacOSButton) Paint() {
        fmt.Println("Rendering a button in a MacOS style.")
    }
    
    type MacOSCheckbox struct{}
    
    func (c MacOSCheckbox) Paint() {
        fmt.Println("Rendering a checkbox in a MacOS style.")
    }
    
    // Abstract factory interface
    type GUIFactory interface {
        CreateButton() Button
        CreateCheckbox() Checkbox
    }
    
    // Concrete factories
    type WindowsFactory struct{}
    
    func (f WindowsFactory) CreateButton() Button {
        return WindowsButton{}
    }
    
    func (f WindowsFactory) CreateCheckbox() Checkbox {
        return WindowsCheckbox{}
    }
    
    type MacOSFactory struct{}
    
    func (f MacOSFactory) CreateButton() Button {
        return MacOSButton{}
    }
    
    func (f MacOSFactory) CreateCheckbox() Checkbox {
        return MacOSCheckbox{}
    }
    
    // Client code
    type Application struct {
        button    Button
        checkbox  Checkbox
    }
    
    func NewApplication(factory GUIFactory) *Application {
        return &Application{
            button:   factory.CreateButton(),
            checkbox: factory.CreateCheckbox(),
        }
    }
    
    func main() {
        app := NewApplication(WindowsFactory{})
        app.button.Paint()
        app.checkbox

     이 예시에서는 추상 팩토리 패턴을 사용하여, 운영체제별로 다른 스타일의 GUI 컴포넌트(버튼)를 생성하는 방법을 보여줍니다. Java와 Go 예시 모두에서, GUIFactory 인터페이스는 운영체제에 맞는 버튼을 생성하는 메서드를 정의하고, 각 운영체제별 팩토리(WindowsFactory, MacOSFactory)가 이 인터페이스를 구현합니다. 클라이언트 코드(Application)는 팩토리 인터페이스를 통해 운영체제에 맞는 버튼을 생성하고 사용합니다. 이를 통해 클라이언트 코드는 생성되는 버튼의 구체적인 클래스에 의존하지 않으며, 확장성과 유연성을 높일 수 있습니다.

    반응형
Designed by Tistory.