본문 바로가기
Programming/Design Pattern

구조 패턴 ( Structural Patterns ) 이란?

by d-e-v-j 2024. 7. 24.
반응형

디자인 패턴의 세 카테고리인

생성, 구조, 행위 중

오늘은 구조 패턴에 대해 간단히 알아보자.


구조 패턴 (Structural Patterns)

  • 클래스와 객체를 조합하여 더 큰 구조를 형성하는 방법
  • 주요 패턴: 어댑터(Adapter), 브리지(Bridge), 컴포지트(Composite), 데코레이터(Decorator), 퍼사드(Facade), 플라이웨이트(Flyweight), 프록시(Proxy)

1. 어댑터 패턴 (Adapter Pattern)

 

  • 목적: 기존 클래스의 인터페이스를 변환하여 호환되지 않는 인터페이스를 가진 클래스들이 함께 동작
  • 사용 예시: 서로 다른 인터페이스를 가진 클래스들을 연결할 때 사용
// 어댑터 패턴 예제
public interface Target {
    void request();
}

public class Adaptee {
    public void specificRequest() {
        System.out.println("Specific request.");
    }
}

public class Adapter implements Target {
    private Adaptee adaptee;

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

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

// 사용 코드
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
target.request(); // "Specific request." 출력

 

 

2. 브릿지 패턴 (Bridge Pattern)

  • 목적: 구현과 추상을 분리하여 독립적으로 변경할 수 있음
  • 사용 예시: GUI 라이브러리에서 플랫폼 독립적인 인터페이스를 제공
// 브리지 패턴 예제
public interface Implementor {
    void operationImpl();
}

public class ConcreteImplementorA implements Implementor {
    @Override
    public void operationImpl() {
        System.out.println("ConcreteImplementorA operation.");
    }
}

public class ConcreteImplementorB implements Implementor {
    @Override
    public void operationImpl() {
        System.out.println("ConcreteImplementorB operation.");
    }
}

public abstract class Abstraction {
    protected Implementor implementor;

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

    public abstract void operation();
}

public class RefinedAbstraction extends Abstraction {
    public RefinedAbstraction(Implementor implementor) {
        super(implementor);
    }

    @Override
    public void operation() {
        implementor.operationImpl();
    }
}

// 사용 코드
Abstraction abstraction1 = new RefinedAbstraction(new ConcreteImplementorA());
abstraction1.operation(); // "ConcreteImplementorA operation." 출력

Abstraction abstraction2 = new RefinedAbstraction(new ConcreteImplementorB());
abstraction2.operation(); // "ConcreteImplementorB operation." 출력
 

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

 

  • 목적: 객체를 트리 구조로 구성하여 부분-전체 계층을 구현
  • 사용 예시: 파일 시스템에서 디렉토리와 파일을 트리 구조로 관리할 때 사용
// 컴포지트 패턴 예제
import java.util.ArrayList;
import java.util.List;

public interface Component {
    void operation();
}

public class Leaf implements Component {
    @Override
    public void operation() {
        System.out.println("Leaf operation.");
    }
}

public 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);
    }

    @Override
    public void operation() {
        for (Component component : children) {
            component.operation();
        }
    }
}

// 사용 코드
Composite root = new Composite();
Leaf leaf1 = new Leaf();
Leaf leaf2 = new Leaf();
root.add(leaf1);
root.add(leaf2);

Composite subtree = new Composite();
subtree.add(new Leaf());
root.add(subtree);

root.operation();
// "Leaf operation."
// "Leaf operation."
// "Leaf operation." 출력

 

 

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

 

  • 목적: 객체에 동적으로 새로운 기능을 추가할 수 있음
  • 사용 예시: GUI 요소에 스크롤바, 테두리 등을 동적으로 추가할 때 사용
// 데코레이터 패턴 예제
public interface Component {
    void operation();
}

public class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("ConcreteComponent operation.");
    }
}

public abstract class Decorator implements Component {
    protected Component component;

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

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

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

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

    private void addedBehavior() {
        System.out.println("ConcreteDecoratorA added behavior.");
    }
}

// 사용 코드
Component component = new ConcreteComponent();
Component decorator = new ConcreteDecoratorA(component);
decorator.operation();
// "ConcreteComponent operation."
// "ConcreteDecoratorA added behavior." 출력

 

 

5. 퍼사드 패턴 (Facade Pattern)

 

  • 목적: 복잡한 서브시스템에 대한 간단한 인터페이스를 제공
  • 사용 예시: 라이브러리나 프레임워크의 복잡한 기능을 단순화하여 제공할 때 사용
// 퍼사드 패턴 예제
public class SubsystemA {
    public void operationA() {
        System.out.println("SubsystemA operation.");
    }
}

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

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

    public void operation() {
        subsystemA.operationA();
        subsystemB.operationB();
    }
}

// 사용 코드
Facade facade = new Facade();
facade.operation();
// "SubsystemA operation."
// "SubsystemB operation." 출력

 

 

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

 

  • 목적: 많은 수의 객체를 효율적으로 지원하기 위해 공유를 사용
  • 사용 예시: 문자 처리 시스템에서 각 문자를 객체로 표현할 때 메모리를 절약하기 위해 사용
// 플라이웨이트 패턴 예제
import java.util.HashMap;
import java.util.Map;

public interface Flyweight {
    void operation(String extrinsicState);
}

public class ConcreteFlyweight implements Flyweight {
    private String intrinsicState;

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

    @Override
    public void operation(String extrinsicState) {
        System.out.println("Intrinsic State: " + intrinsicState + ", Extrinsic State: " + extrinsicState);
    }
}

public 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);
    }
}

// 사용 코드
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight1 = factory.getFlyweight("state1");
Flyweight flyweight2 = factory.getFlyweight("state2");
Flyweight flyweight3 = factory.getFlyweight("state1");

flyweight1.operation("extrinsic1");
flyweight2.operation("extrinsic2");
flyweight3.operation("extrinsic3");
// "Intrinsic State: state1, Extrinsic State: extrinsic1"
// "Intrinsic State: state2, Extrinsic State: extrinsic2"
// "Intrinsic State: state1, Extrinsic State: extrinsic3" 출력

 

 

7. 프록시 패턴 (Proxy Pattern)

 

  • 목적: 다른 객체에 대한 접근을 제어하기 위해 대리자를 제공
  • 사용 예시: 원격 프록시, 가상 프록시, 보호 프록시 등을 구현할 때 사용
// 프록시 패턴 예제
public interface Subject {
    void request();
}

public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject request.");
    }
}

public class Proxy implements Subject {
    private RealSubject realSubject;

    @Override
    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.request();
    }
}

// 사용 코드
Subject proxy = new Proxy();
proxy.request(); // "RealSubject request." 출력

 


예전에 공부할 때 뭘 이런걸 외워야하나 했었는데

구조를 설계하는 데 도움이 되고 

확장성, 유지보수 측면에서도 많은 도움이 되는 패턴들이다.

 

아는 만큼 보이고 아는 만큼 편리해지는 것 같다.

 

728x90
반응형
LIST