[디자인패턴] 구조 패턴 (Structural Patterns)

엔스마트·2024년 6월 25일

디자인 패턴

목록 보기
3/4

구조 패턴(Structural Patterns)은 클래스와 객체를 조합하여 더 큰 구조를 만드는 데 중점을 둡니다. 이 패턴들은 클래스 간의 관계를 단순화하고 효율적으로 만들어 시스템의 유연성과 재사용성을 높이는 데 기여합니다. 아래에서는 주요 구조 패턴을 상세히 분석하겠습니다.

1. 어댑터 패턴 (Adapter Pattern)

  • 목적: 호환성이 없는 인터페이스를 가진 클래스들이 함께 동작할 수 있도록 합니다.
  • 구현:
    • 클래스 어댑터: 상속을 통해 어댑터를 구현.
    • 객체 어댑터: 합성을 통해 어댑터를 구현.
// 기존 인터페이스
interface Target {
    void request();
}

// 기존 클래스
class Adaptee {
    public void specificRequest() {
        System.out.println("Specific request");
    }
}

// 클래스 어댑터
class Adapter extends Adaptee implements Target {
    public void request() {
        specificRequest();
    }
}

// 객체 어댑터
class Adapter implements Target {
    private Adaptee adaptee;

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

    public void request() {
        adaptee.specificRequest();
    }
}
  • 활용 예시: 레거시 코드와 새로운 코드 통합, 서로 다른 API 간의 호환성 제공.

2. 브리지 패턴 (Bridge Pattern)

  • 목적: 구현부와 추상층을 분리하여 독립적으로 변형될 수 있게 합니다.
  • 구현:
    추상 클래스와 구현 클래스를 별도로 정의하고, 구현 클래스를 추상 클래스에 포함시킴.
// 구현부 인터페이스
interface Implementor {
    void operationImpl();
}

// 구체적인 구현부
class ConcreteImplementorA implements Implementor {
    public void operationImpl() {
        System.out.println("ConcreteImplementorA 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);
    }

    public void operation() {
        implementor.operationImpl();
    }
}
  • 활용 예시: GUI 시스템에서 플랫폼 독립적인 인터페이스 제공.

3. 컴포지트 패턴 (Composite Pattern)

  • 목적: 객체를 트리 구조로 구성하여 부분-전체 계층을 표현합니다.
  • 구현:
    공통 인터페이스 또는 추상 클래스를 정의하고, 이를 구현하는 개별 객체와 복합 객체를 정의.
// 구성 요소 인터페이스
interface Component {
    void operation();
}

// 개별 객체
class Leaf implements Component {
    public void operation() {
        System.out.println("Leaf operation");
    }
}

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

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

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

    public void operation() {
        for (Component child : children) {
            child.operation();
        }
    }
}
  • 활용 예시: 파일 시스템에서 디렉토리와 파일의 계층 구조 표현.

4. 데코레이터 패턴 (Decorator Pattern)

  • 목적: 객체에 동적으로 새로운 기능을 추가합니다.
  • 구현:
    기본 클래스와 데코레이터 클래스를 정의. 데코레이터 클래스는 기본 클래스를 포함하고, 해당 클래스를 확장.
// 구성 요소 인터페이스
interface Component {
    void operation();
}

// 기본 구성 요소
class ConcreteComponent implements Component {
    public void operation() {
        System.out.println("ConcreteComponent operation");
    }
}

// 데코레이터 클래스
abstract class Decorator implements Component {
    protected Component component;

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

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

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

    public void operation() {
        super.operation();
        addedBehavior();
    }

    private void addedBehavior() {
        System.out.println("ConcreteDecoratorA added behavior");
    }
}
  • 활용 예시: GUI 컴포넌트에 스크롤바, 테두리 등을 동적으로 추가할 때.

5. 퍼사드 패턴 (Facade Pattern)

  • 목적: 복잡한 서브시스템에 대한 간단한 인터페이스를 제공합니다.
  • 구현:
    여러 클래스의 복잡한 기능을 단순화하여 제공하는 퍼사드 클래스를 정의.
// 서브시스템 클래스
class SubsystemA {
    public void operationA() {
        System.out.println("SubsystemA operation");
    }
}

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

// 퍼사드 클래스
class Facade {
    private SubsystemA subsystemA;
    private SubsystemB subsystemB;

    public Facade() {
        subsystemA = new SubsystemA();
        subsystemB = new SubsystemB();
    }

    public void operation() {
        subsystemA.operationA();
        subsystemB.operationB();
    }
}
  • 활용 예시: 라이브러리나 프레임워크의 단순화된 API 제공.

6. 플라이웨이트 패턴 (Flyweight Pattern)

  • 목적: 다수의 작은 객체를 효율적으로 공유하여 메모리 사용을 줄입니다.
  • 구현:
    공유 객체와 비공유 객체를 구분하여 정의. 공유 객체는 상태를 외부에서 전달받아 사용.
// 플라이웨이트 인터페이스
interface Flyweight {
    void operation(String extrinsicState);
}

// 구체적인 플라이웨이트
class ConcreteFlyweight implements Flyweight {
    private String intrinsicState;

    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    public void operation(String extrinsicState) {
        System.out.println("IntrinsicState: " + intrinsicState + ", ExtrinsicState: " + extrinsicState);
    }
}

// 플라이웨이트 팩토리
class FlyweightFactory {
    private Map<String, Flyweight> flyweights = new HashMap<>();

    public Flyweight getFlyweight(String key) {
        if (!flyweights.containsKey(key)) {
            flyweights.put(key, new ConcreteFlyweight(key));
        }
        return flyweights.get(key);
    }
}
  • 활용 예시: 텍스트 편집기에서 문자 객체를 효율적으로 관리.

7. 프록시 패턴 (Proxy Pattern)

  • 목적: 실제 객체에 대한 대리자 객체를 제공하여 접근을 제어합니다.
  • 구현:
    실제 객체와 동일한 인터페이스를 구현하는 프록시 클래스를 정의.
// 실제 객체 인터페이스
interface RealSubject {
    void request();
}

// 실제 객체
class RealSubjectImpl implements RealSubject {
    public void request() {
        System.out.println("RealSubject request");
    }
}

// 프록시 객체
class Proxy implements RealSubject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    public void request() {
        System.out.println("Proxy request");
        realSubject.request();
    }
}
  • 활용 예시: 원격 프록시, 가상 프록시, 보호 프록시

구조 패턴의 장점과 단점

장점

  • 코드 재사용성: 공통된 구조를 통해 코드 재사용성을 높입니다.
  • 유연성 증가: 시스템 구조를 유연하게 구성하여 변경 요구에 쉽게 대응할 수 있습니다.
  • 응집력 향상: 객체나 클래스 간의 관계를 명확하게 정의하여 시스템의 응집력을 높입니다.

단점

  • 복잡성 증가: 구조 패턴을 적용하면서 추가적인 클래스나 인터페이스가 필요하여 코드의 복잡성이 증가할 수 있습니다.
  • 성능 문제: 일부 구조 패턴은 객체 간의 상호작용을 증가시켜 성능에 영향을 미칠 수 있습니다.

구조 패턴은 객체 지향 설계에서 중요한 역할을 하며, 다양한 상황에서 유연하고 효율적인 시스템을 설계하는 데 도움을 줍니다. 각 패턴을 적절히 사용하여 코드의 가독성, 유지 보수성, 확장성을 향상시키는 것이 중요합니다.

profile
클라우드 전환, MSA 서비스, DevOps 환경 구축과 기술지원 그리고 엔터프라이즈 시스템을 구축하는 최고 실력과 경험을 가진 Architect Group 입니다.

0개의 댓글