반응형
디자인 패턴의 세 카테고리인
생성, 구조, 행위 중
오늘은 구조 패턴에 대해 간단히 알아보자.
구조 패턴 (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