본문 바로가기
source-code/software

[정보처리기사 실기] GoF 디자인 패턴

by mattew4483 2023. 9. 11.
728x90
반응형

Gang of Four

생성 패턴

→ 객체의 인스턴스 생성에 관여하고, 클래스 정의와 객체 생성 방식을 구조화, 캡슐화하는 패턴

추상 팩토리(Abstract Factory)

서로 연관되거나 의존적인 객체의 집합을 생성하기 위한 인터페이스를 제공(구체적 클래스에 의존X)

동일 주제의 다른 팩토리를 묶음

더보기

예제 코드

// Abstract Factory 인터페이스
interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

// Concrete Factory 1
// Abstract Factory 인터페이스를 구현한 구체적인 클래스입니다. 
// 각각의 Concrete Factory는 특정 종류의 객체를 생성합니다.
class WindowsFactory implements GUIFactory {
    public Button createButton() {
        return new WindowsButton();
    }

    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }
}

// Concrete Factory 2
class MacOSFactory implements GUIFactory {
    public Button createButton() {
        return new MacOSButton();
    }

    public Checkbox createCheckbox() {
        return new MacOSCheckbox();
    }
}

// Abstract Product: 버튼 인터페이스
// 생성될 객체의 인터페이스를 정의
interface Button {
    void paint();
}

// Concrete Product 1: Windows 버튼
// Abstract Product를 구현한 실제 클래스입니다. 추상 팩토리에 의해 생성됩니다.
class WindowsButton implements Button {
    public void paint() {
        System.out.println("Render a Windows button");
    }
}

// Concrete Product 2: MacOS 버튼
class MacOSButton implements Button {
    public void paint() {
        System.out.println("Render a MacOS button");
    }
}

// Abstract Product: 체크박스 인터페이스
interface Checkbox {
    void paint();
}

// Concrete Product 1: Windows 체크박스
class WindowsCheckbox implements Checkbox {
    public void paint() {
        System.out.println("Render a Windows checkbox");
    }
}

// Concrete Product 2: MacOS 체크박스
class MacOSCheckbox implements Checkbox {
    public void paint() {
        System.out.println("Render a MacOS checkbox");
    }
}

// 클라이언트 코드
public class Application {
    private GUIFactory factory;
    private Button button;
    private Checkbox checkbox;

    public Application(GUIFactory factory) {
        this.factory = factory;
        this.button = factory.createButton();
        this.checkbox = factory.createCheckbox();
    }

    public void createUI() {
        button.paint();
        checkbox.paint();
    }

    public static void main(String[] args) {
        // Windows 스타일 UI 생성
        Application app1 = new Application(new WindowsFactory());
        app1.createUI();

        // MacOS 스타일 UI 생성
        Application app2 = new Application(new MacOSFactory());
        app2.createUI();
    }
}

빌더 (Builder)

복잡한 객체의 생성 과정과 구현 방법을 분리해서 관리

객체를 조립하거나 생성하는 과정을 단순화하는데 사용됨

더보기
// 복잡한 객체의 생성 과정을 추상화한 인터페이스
interface Builder {
    void buildPart1();
    void buildPart2();
    Product getResult();
}

// 구체적인 Builder 클래스
class ConcreteBuilder implements Builder {
    private Product product = new Product();

    @Override
    public void buildPart1() {
        product.setPart1("Part 1 built");
    }

    @Override
    public void buildPart2() {
        product.setPart2("Part 2 built");
    }

    @Override
    public Product getResult() {
        return product;
    }
}

// 생성된 객체를 나타내는 클래스
class Product {
    private String part1;
    private String part2;

    public void setPart1(String part1) {
        this.part1 = part1;
    }

    public void setPart2(String part2) {
        this.part2 = part2;
    }

    public void show() {
        System.out.println("Part 1: " + part1);
        System.out.println("Part 2: " + part2);
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        Builder builder = new ConcreteBuilder();

        // 복잡한 객체 생성 과정
        builder.buildPart1();
        builder.buildPart2();

        // 생성된 객체 획득
        Product product = builder.getResult();

        // 생성된 객체 사용
        product.show();
    }
}

팩토리 메서드 (Factory Method)

객체 생성을 캡슐화 → 인터페이스와 실제 객체를 생성하는 클래스를 분리

상위 클래스에서는 객체를 생성하기 위한 인터페이스(객체를 만드는 방법)를 정의

서브 클래스에서는 실제 생성되는 객체의 타입을 결정(객체를 생성)

더보기
// 객체 생성을 위한 인터페이스
interface Product {
    void create();
}

// 구체적인 Product 구현 클래스
class ConcreteProductA implements Product {
    @Override
    public void create() {
        System.out.println("Product A created");
    }
}

class ConcreteProductB implements Product {
    @Override
    public void create() {
        System.out.println("Product B created");
    }
}

// Factory Method를 정의하는 인터페이스
interface Creator {
    Product factoryMethod();
}

// 구체적인 Creator 클래스
class ConcreteCreatorA implements Creator {
    @Override
    public Product factoryMethod() {
        return new ConcreteProductA();
    }
}

class ConcreteCreatorB implements Creator {
    @Override
    public Product factoryMethod() {
        return new ConcreteProductB();
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        Creator creatorA = new ConcreteCreatorA();
        Product productA = creatorA.factoryMethod();
        productA.create();

        Creator creatorB = new ConcreteCreatorB();
        Product productB = creatorB.factoryMethod();
        productB.create();
    }
}

프로토타입 (Prototype)

기존 객체를 복제하여, 새로운 객체를 생성하는 방법 제공

객체를 복제하기 위해 원본 객체의 프로토타입을 사용

객체 생성 비용을 줄이고, 객체를 동적으로 생성할 수 있음

더보기
// Cloneable 인터페이스를 구현한 프로토타입 클래스
class Prototype implements Cloneable {
    private String data;

    public Prototype(String data) {
        this.data = data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public String getData() {
        return data;
    }

    // 객체 복제 메소드
    @Override
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 원본 객체 생성
        Prototype original = new Prototype("Original Data");

        // 원본 객체를 복제하여 새로운 객체 생성
        Prototype copy = original.clone();

        // 복제된 객체의 데이터 변경
        copy.setData("Copied Data");

        // 원본과 복제본 데이터 출력
        System.out.println("Original Data: " + original.getData());
        System.out.println("Copied Data: " + copy.getData());
    }
}

싱글톤 (Singleton)

특정 클래스의 객체가 애플리케이션 내에서 오직 하나만 생성되도록 보장

애플리케이션에서 공통된 자원을 공유하거나 설정 정보를 단일 객체로 관리할 때 사용

더보기
public class Singleton {
    // 유일한 인스턴스를 저장할 정적 변수
    private static Singleton uniqueInstance;

    // 생성자를 private으로 선언하여 외부에서 직접 인스턴스를 생성하는 것을 방지
    private Singleton() {
    }

    // 유일한 인스턴스를 반환하는 정적 메서드
    public static Singleton getInstance() {
        // 인스턴스가 생성되어 있지 않으면 생성하고, 이미 생성되어 있는 경우 기존 인스턴스 반환
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }

    // 다른 메서드나 속성을 정의할 수 있음
    public void doSomething() {
        System.out.println("Singleton is doing something.");
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        // Singleton 인스턴스 얻기
        Singleton singleton = Singleton.getInstance();

        // 인스턴스를 사용하여 작업 수행
        singleton.doSomething();
    }
}

구조 패턴

어댑터 (Adapter)

서로 호환되지 않는 인터페이스를 가진 두 개의 클래스를 연결해 함께 동작할 수 있도록 만듬

인터페이스가 호환되지 않는 클래스들을 함께 이용할 수 있도록, 타 클래스의 인터페이스를 기존 인터페이스에 덧씌움

더보기
// 호환되지 않는 외부 라이브러리의 클래스
class Adaptee {
    public void specificRequest() {
        System.out.println("Specific request from Adaptee.");
    }
}

// 클라이언트가 사용할 수 있는 새로운 인터페이스
interface Target {
    void request();
}

// Adapter 클래스
class Adapter implements Target {
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        // Adaptee의 메서드를 호출하여 호환성을 제공
        adaptee.specificRequest();
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        // 호환되지 않는 Adaptee 객체 생성
        Adaptee adaptee = new Adaptee();

        // Adapter를 사용하여 호환성 제공
        Target target = new Adapter(adaptee);

        // 클라이언트 코드는 Target 인터페이스를 통해 요청
        target.request();
    }
}

브릿지 (Bridge)

추상 클래스(추상화)와 구체 클래스(구현)를 별도의 계층으로 나누고, 추상화된 객체와 구현된 객체를 연결하는 인터페이스를 사용

두 클래스 간 결합을 느슨하게 유지함으로써, 변경에 용이

더보기
// 구현 인터페이스
interface Implementor {
    void operationImpl();
}

// 구현 클래스 A
class ConcreteImplementorA implements Implementor {
    @Override
    public void operationImpl() {
        System.out.println("Concrete Implementor A operation");
    }
}

// 구현 클래스 B
class ConcreteImplementorB implements Implementor {
    @Override
    public void operationImpl() {
        System.out.println("Concrete Implementor B operation");
    }
}

// 추상화 클래스
abstract class Abstraction {
    protected Implementor implementor;

    public Abstraction(Implementor implementor) {
        this.implementor = implementor;
    }

    public abstract void operation();
}

// 추상화 클래스의 확장
class RefinedAbstraction extends Abstraction {
    public RefinedAbstraction(Implementor implementor) {
        super(implementor);
    }

    @Override
    public void operation() {
        System.out.println("Refined Abstraction operation");
        implementor.operationImpl();
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        Implementor implementorA = new ConcreteImplementorA();
        Abstraction abstractionA = new RefinedAbstraction(implementorA);
        abstractionA.operation();

        Implementor implementorB = new ConcreteImplementorB();
        Abstraction abstractionB = new RefinedAbstraction(implementorB);
        abstractionB.operation();
    }
}

컴포지트 (Composite)

객체 구조를 트리 구조로 구성

개별 객체와 복합 객체(컨테이너)를 동일한 인터페이스로 표현

클라이언트가 단일 객체와 복합 객체를 동일하게 다룰 수 있도록 함

더보기
import java.util.ArrayList;
import java.util.List;

// Component 인터페이스 (개별 객체와 복합 객체의 공통 인터페이스)
interface Component {
    void operation();
}

// Leaf 클래스 (개별 객체)
class Leaf implements Component {
    private String name;

    public Leaf(String name) {
        this.name = name;
    }

    @Override
    public void operation() {
        System.out.println("Leaf " + name + " operation");
    }
}

// Composite 클래스 (복합 객체)
class Composite implements Component {
    private List<Component> components = new ArrayList<>();

    public void add(Component component) {
        components.add(component);
    }

    public void remove(Component component) {
        components.remove(component);
    }

    @Override
    public void operation() {
        System.out.println("Composite operation");
        for (Component component : components) {
            component.operation();
        }
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        // Leaf 객체 생성
        Component leaf1 = new Leaf("A");
        Component leaf2 = new Leaf("B");

        // 복합 객체 생성
        Composite composite = new Composite();
        composite.add(leaf1);
        composite.add(leaf2);

        // 복합 객체 내부의 개별 객체와 복합 객체 모두 동일한 인터페이스로 다룸
        Component leaf3 = new Leaf("C");
        composite.add(leaf3);

        // 개별 객체와 복합 객체를 모두 조작
        composite.operation();
    }
}

데코레이터 (Decorator)

기본 객체와 동일한 인터페이스를 가지는 데코레이터 클래스를 사용해 객체의 동작을 확장

상속을 사용하지 않고, 객체 간의 결합을 통해 객체의 동작을 수정하거나 확장

객체의 구조를 유지한 채로 새로운 동작을 추가할 때 유용함

더보기
// Component 인터페이스 (기본 객체와 데코레이터 클래스의 공통 인터페이스)
interface Component {
    void operation();
}

// ConcreteComponent 클래스 (기본 객체)
class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("ConcreteComponent operation");
    }
}

// Decorator 클래스 (데코레이터의 기반 클래스)
class Decorator implements Component {
    protected Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        component.operation();
    }
}

// ConcreteDecorator 클래스 (구체적인 데코레이터)
class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("ConcreteDecoratorA operation");
    }
}

class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("ConcreteDecoratorB operation");
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        // 기본 객체 생성
        Component component = new ConcreteComponent();

        // 데코레이터를 사용하여 기본 객체에 새로운 기능 추가
        Component decoratedComponentA = new ConcreteDecoratorA(component);
        Component decoratedComponentB = new ConcreteDecoratorB(decoratedComponentA);

        // 데코레이터를 통해 확장된 동작 수행
        decoratedComponentB.operation();
    }
}

퍼싸드 (Facade)

복잡한 시스템 내부의 다수의 클래스나 서브시스템을 하나의 간단한 인터페이스로 제공

클라이언트는 복잡한 내부 구조를 알 필요 없이, 간편한 방식으로 시스템과 상호작용

더보기
// 복잡한 서브시스템의 클래스들
class SubSystemA {
    void operationA() {
        System.out.println("SubSystemA operation");
    }
}

class SubSystemB {
    void operationB() {
        System.out.println("SubSystemB operation");
    }
}

class SubSystemC {
    void operationC() {
        System.out.println("SubSystemC operation");
    }
}

// Facade 클래스
class Facade {
    private SubSystemA subsystemA;
    private SubSystemB subsystemB;
    private SubSystemC subsystemC;

    public Facade() {
        this.subsystemA = new SubSystemA();
        this.subsystemB = new SubSystemB();
        this.subsystemC = new SubSystemC();
    }

    // 복잡한 동작을 간단한 인터페이스로 제공
    public void doComplexOperation() {
        System.out.println("Facade is simplifying the complex operation.");
        subsystemA.operationA();
        subsystemB.operationB();
        subsystemC.operationC();
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        Facade facade = new Facade();

        // Facade를 통해 복잡한 동작 실행
        facade.doComplexOperation();
    }
}

플라이웨이트 (Flyweight)

객체를 공유하여 메모리 사용량을 최소화하고 성능을 향상

많은 수의 객체를 생성하고 관리할 때 유용하며, 객체의 내부 상태와 외부 상태를 분리하여 처리

더보기
import java.util.HashMap;
import java.util.Map;

// 플라이웨이트 패턴을 구현하기 위한 인터페이스
interface Shape {
    void draw();
}

// 구체적인 플라이웨이트 객체 (내부 상태 공유)
class ConcreteShape implements Shape {
    private String type;

    public ConcreteShape(String type) {
        this.type = type;
    }

    @Override
    public void draw() {
        System.out.println("Drawing " + type + " shape.");
    }
}

// 플라이웨이트 팩토리
class ShapeFactory {
    private static final Map<String, Shape> shapes = new HashMap<>();

    public static Shape getShape(String type) {
        Shape shape = shapes.get(type);

        if (shape == null) {
            shape = new ConcreteShape(type);
            shapes.put(type, shape);
        }

        return shape;
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        // 플라이웨이트 패턴을 사용하여 동일한 내부 상태를 가진 객체를 공유
        Shape circle1 = ShapeFactory.getShape("Circle");
        Shape circle2 = ShapeFactory.getShape("Circle");
        Shape triangle = ShapeFactory.getShape("Triangle");

        // 각 객체를 그립니다.
        circle1.draw();
        circle2.draw();
        triangle.draw();
    }
}

프록시 (Proxy)

다른 객체에 대한 접근을 제어하거나 대리할 때 사용되는 패턴

대리자(proxy) 객체는 실제 객체에 대한 접근을 제어하며, 클라이언트와 실제 객체 사이에서 중간 역할을 수행

실제 객체에 대한 접근 이전에 필요한 행동을 취할 수 있게 만듬 → 접근 조절, 비용 절감, 복잡도 감소

더보기
// Subject 인터페이스 (실제 객체와 프록시 객체가 구현하는 공통 인터페이스)
interface Subject {
    void request();
}

// RealSubject 클래스 (실제 객체)
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject: Handling request.");
    }
}

// Proxy 클래스
class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy() {
        // 필요한 시점에 실제 객체 생성
        realSubject = new RealSubject();
    }

    @Override
    public void request() {
        // 실제 객체에 대한 접근 제어 및 추가 동작 수행
        System.out.println("Proxy: Pre-processing request.");
        realSubject.request();
        System.out.println("Proxy: Post-processing request.");
    }
}

// 클라이언트 코드
public class Client {
    public static void main(String[] args) {
        // Proxy를 통해 실제 객체에 접근
        Subject proxy = new Proxy();

        // 클라이언트는 Proxy를 통해 request를 호출
        proxy.request();
    }
}

행위 패턴

커맨드 (Command)

요청을 객체로 캡슐화 → 실행할 작업을 나중에 지정하거나 연기할 수 있게 함

하나의 추상 클래스에 메서드를 만들어, 각 명령이 들어오면 그에 맞는 서브 클래스가 선택 되어 실행됨

실행 취소, 큐 관리, 로깅 등의 추가에 용이

인터프리터 (Interpreter)

복잡한 언어나 문법을 해석하고 실행하는 디자인 패턴

표현식을 객체로 표현하고, 각 표현식을 해석하여 실행하는데 사용됨

이터레이터 (Iterator)

‘반복자’ 역할을 하는 객체를 사용하여 컬렉션의 요소에 접근하고 순회하는 방식을 추상화

객체의 집합체(컬렉션)를 순차적으로 접근하고 조작할 수 있는 인터페이스를 제공

중재자 (Mediator)

객체 간의 상호 작용을 중앙 집중화하고, 객체들간의 직접적인 통신을 방지하기 위함

객체들은 중앙 조정자(mediator)를 통해 통신 → 변경 사항이 애플리케이션에 미치는 영향 줄일 수 있음

메멘토 (Memento)

클래스 설계 관점에서 객체의 정보를 저장할 필요가 있을 때 적용하는 디자인 패턴

Undo 기능을 개발할 때 사용

객체를 이전 상태로 복구 시켜야 하는 경우 Undo(작업 취소) 요청 기능

옵저버 (Observer)

어떤 클래스에 변화가 일어났을 때, 이를 감지하여 다른 클래스에 통보해주는 것

한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에게 연락이 가고 자동으로 내용이 갱신되는 패턴

일대다의 의존성을 가지고 상호작용하는 객체 사이에서는 가능한 느슨하게 결합하는 디자인 패턴

상태 (State)

객체 상태를 캡슐화 해서 클래스화함으로써 그것을 참조하게 하는 방 식

상태에 따라 다르게 처리할 수 있도록 행위 내용을 변경하고, 변경시 원시 코드의 수정을 최소화 할 수 있고 유지보수의 편의성도 가짐

객체의 상태에 따라 행위 내용을 변경

전략 (Strategy)

알고리즘 군을 정의하고(추상클래스) 같은 알고리즘을 각각 하나의 클래스로 캡슐화한 후, 필요할 때 서로 교환해서 사용 할 수 있게 하는 패턴

행위 클래스로 캡슐화해 동적으로 행위를 자유롭게 바꿀 수 있게 해주는 패턴

템플릿 메서드 (Templete Method)

상위 클래스에서는 추상적으로 표현하고, 그 구체적인 내용은 하위 클래스에서 결정되는 디자인 패턴

어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴(상위 작업의 구조를 바꾸지 않으면서 서브 클래스로 작업의 일부분을 수행)

비지터 (Visitor)

각 클래스의 데이터 구조로부터 처리 기능을 분리하여 별도의 클래스를 만들어 놓고 해당 클래스의 메서드가 각 클래스를 돌아다니며 특정 작업을 수행하도록 만드는 패턴

객체의 구조는 변경하지 않으면서 기능만 따로 추가하거나 확장할 때 사용하는 패턴

특정 구조를 이루는 복합 객체의 원소 특성에 따라 동작을 수행할 수 있도록 지원

728x90
반응형