[내일배움캠프 Day15] 디자인 패턴 - 구조 패턴

2025. 1. 9. 22:54·내일배움캠프/TIL

이어서  '포르잔 C++ 바이블' 책에 있는 디자인 패턴 내용 중 구조 패턴에 대해 정리하였습니다. 

 

1. 구조 패턴의 개요

적절한 자료형이 없는 상태에서 문제를 해결해야 하는 경우, 이미 존재하는 인터페이스와 구현을 결합해서 새로운 자료형을 만들어 문제를 해결합니다. 구조 패턴은 이런 경우 사용하는 방법으로 상속과 구성을 결합해 사용하는 방법을 정리한 것입니다.

 

2. 브리지 패턴

브리지 패턴은 새로운 인터페이스를 만들 때, 이전 구현을 변경하지 않고 간단하게 구현할 수 있게 해줍니다. 새로운 인터페이스가 기존의 인터페이스를 구성으로 포함하고, 새로운 인터페이스 내부의 함수에서 기존의 인터페이스가 갖는 함수를 호출하는 것입니다.

브릿지 패턴의 예

 

3. 어댑터 패턴

기존의 어떤 클래스를 사용해야 하는데 인터페이스가 우리가 원하는 형태가 아닐 수 있습니다. 이때 기존의 인터페이스를 우리가 원하는 인터페이스로 변경하게 하는 어댑터를 끼워줄 수 있습니다. 이런 어댑터를 활용하는 패턴이 **어댑터 패턴(adapter pattern)**입니다.

어댑터 패턴의 UML 다이어그램

Adaptee 객체를 사용하려는데 인터페이스가 원하는 인터페이스가 아니어서 Adapter 클래스를 만들어 중간에 사용하는 것입니다.

 

4. 퍼사드 패턴

퍼사드 패턴은 서브 시스템에 하나의 통합된 인터페이스를 제공하는 패턴입니다.

출처 : https://refactoring.guru/ko/design-patterns/facade

VideoConverter 클래스 하나만 제공하고, 사용자의 요청 형태에 따라서 적절한 함수를 호출하는 형태가 더 사용하기 쉽습니다.

 

예제 코드

더보기

인터페이스 파일(facade.h)

/**************************************************************
 * House(퍼사드) 클래스와 4개의 방 클래스의 인터페이스 파일   *
 *                                                            *
 **************************************************************/
#ifndef FACADE_H
#define FACADE_H
#include <string>
#include <iostream>
using namespace std;

// LivingRoom 클래스
class LivingRoom
{  
  private: 
    double size;
  public:
   LivingRoom(double size); 
   void draw();
};
// BedRoom 클래스
class BedRoom 
{
  private: 
    double size;
  public:
   BedRoom(double size); 
  void draw();
};
// Kitchen 클래스
class Kitchen
{
  private: 
    double size;
  public:
    Kitchen(double size); 
    void draw();
};
// BathRoom 클래스
class BathRoom
{
  private: 
    double size;
  public:
   BathRoom(double size); 
  void draw();
};
// House(퍼사드) 클래스
class House 
{
  private: 
    double size;
  public:
    House(double size); 
    void draw();
};
#endif

구현 파일(facade.cpp)

/**************************************************************
 * 퍼사드 패턴 예제의 구현 파일                               *
 **************************************************************/
#include "facade.h"

// LivingRoom의 생성자
LivingRoom::LivingRoom(double s)
:size(s)
{
}
// LivingRoom의 draw 멤버 함수
void LivingRoom::draw()
{
  cout << "거실의 크기는 " << size;
  cout << "평방 피트로 그립니다." << endl;
}
// BedRoom의 생성자
BedRoom::BedRoom(double s)
:size(s)
{
}
// BedRoom의 draw 멤버 함수
void BedRoom::draw()
{
  cout << "침실의 크기는 " << size ;
  cout << "평방 피트로 그립니다." << endl;
}
// Kitchen의 생성자
Kitchen::Kitchen(double s)
:size(s)
{
}
// Kitchen의 draw 멤버 함수
void Kitchen::draw()
{
  cout << "부엌의 크기는 " << size;
  cout << "평방 피트로 그립니다." << endl;
}
// BathRoom의 생성자
BathRoom::BathRoom(double s)
:size(s)
{
}
// BathRoom의 draw 멤버 함수
void BathRoom::draw()
{
  cout << "욕실의 크기는 " << size;
  cout << "평방 피트로 그립니다." << endl;
}
// House(facade)의 생성자
House::House(double s)
:size(s)
{
}
// House의 draw 멤버 함수
void House::draw()
{
  LivingRoom livingRm(size * 0.35);
  livingRm.draw();
  BedRoom bedRm(size * 0.35);
  bedRm.draw();
  Kitchen kitchen(size * 0.15);
  kitchen.draw();
  BathRoom BathRm(size * 0.15);
  BathRm.draw();
}

애플리케이션 파일(app.cpp)

/**************************************************************
 * 퍼사드 패턴을 사용하는 애플리케이션 파일                   *
 **************************************************************/
#include "facade.h"

int main()
{
  House house(1000);  // House(퍼사드) 객체 생성
  house.draw(); // draw 멤버 함수 호출
  return 0;
}

실행 결과

거실의 크기는 350평방 피트로 그립니다.
침실의 크기는 350평방 피트로 그립니다.
부엌의 크기는 150평방 피트로 그립니다.
욕실의 크기는 150평방 피트로 그립니다.

 

5. 프록시 패턴

프록시는 대리인을 뜻하는 말입니다. 컴퓨터 프로그래밍에서도 실제 객체의 일을 다른 객체가 하도록 프록시 객체를 사용합니다.

프록시 패턴은 대부분 Proxy 클래스의 멤버 함수만 사용합니다. Proxy 객체는 필요한 경우에만 Real 객체를 만들어서 사용합니다. 그래픽 객체는 굉장히 복잡하고 무거워, 일반적으로 움직임을 구현할 때 객체를 생성하는 처리와 이동하는 처리가 분리되어 있습니다. 그래서 Proxy 객체를 사용해서 대부분의 처리를 하고, 필요할 때만 무거운 Real 객체를 생성해서 처리합니다.

 

예제 코드

더보기

인터페이스 파일(proxy.h)

/**************************************************************
 * 프록시 패턴 예제의 인터페이스 파일                         *
 **************************************************************/
#ifndef PROXY_H
#define PROXY_H
#include <string>
#include <iostream>
using namespace std;

// Subject 추상 클래스 선언
class Subject
{  
  public:
    virtual void draw(int x, int y) = 0; 
    virtual void erase() = 0;
    virtual ~Subject() {}
};
// Real 클래스 선언
class Real : public Subject 
{
  public:
    void draw(int x, int y); 
    void erase();
};
// Proxy 클래스 선언
class Proxy : public Subject
{
  private: 
    Subject* real;
  public:
    Proxy();
    ~Proxy();
    void draw(int x, int y);
    void erase();
};
#endif

구현 파일(proxy.cpp)

/**************************************************************
 * 프록시 패턴 예제의 구현 파일                               *
 **************************************************************/
#include <string>
#include "proxy.h"

// Real의 draw 멤버 함수
void Real::draw(int x, int y)
{
  cout << "객체를 " << x << ", " << y << "위치에 그렸습니다." << endl;
} 
// Real의 erase 멤버 함수
void Real::erase()
{
  cout << "객체를 지웠습니다." << endl;
}
// Proxy의 생성자
Proxy::Proxy()
{
  real = 0;
}
// Proxy의 소멸자
Proxy::~Proxy()
{
  delete real;
}
// Proxy의 draw 멤버 함수
void Proxy::draw(int x, int y)
{
  if(real == 0 )
  {
    real = new Real;
  }
  real->draw(x, y);
}
// Proxy의 erase 멤버 함수
void Proxy::erase()
{
  real->erase();
}

애플리케이션 파일(app.cpp)

/**************************************************************
 * 프록시 패턴 예제의 애플리케이션 파일                       *
 **************************************************************/
#include <iostream>
#include "proxy.h"
using namespace std;

int main()
{
  Proxy p;
  for(int i = 0 ; i < 5 ; i++)
  {
    p.draw(i, i);
    p.erase();
 }
 return 0;
}

Real 객체가 존재하지 않아도, Proxy 클래스의 draw 함수가 Real 객체를 생성해줍니다. 객체를 그리는 것(Proxy::draw)과 인스턴스를 그리는 것(Real:draw)이 분리되어 있다는 것을 확인할 수 있습니다.

 

6. 데코레이터 패턴

**데코레이터 패턴(decorator pattern)**을 사용하면, 런타임 때 객체를 데코레이션할 수 있습니다. 데코레이션한다는 말은 꾸민다는 뜻으로 추가적인 특징을 부여한다는 의미입니다. 여러 개의 다른 특징을 가진 객체를 만드는 것이 아니라, 하나의 객체를 여러 객체로 꾸며서 특징을 추가한다는 것입니다.

데코레이터 패턴은 GUI 애플리케이션에서 많이 사용됩니다. 여러 위젯(버튼, 테이블 등)은 여러 테두리 모양(두껍거나 얇은 테두리 등)을 가질 수 있습니다. 위젯이 있어야 테두리를 선택할 수 있으며, 테두리는 테두리만으로는 사용할 수 없습니다.

Component 객체의 draw 함수는 자기 자신을 그리고, Decorator 객체의 draw 함수는 갖고 있는 Component 객체의 draw 함수를 호출합니다. 즉 Decorator 객체가 Component 객체를 감싸고 있는 것(랩핑 wrapping)입니다.

 

예제 코드

더보기

인터페이스 파일(decorator.h)

/**************************************************************
 * 데코레이터 패턴 예제의 인터페이스 파일                     *
 **************************************************************/
#ifndef DECORATOR_H
#define DECORATOR_H
#include <string>
#include <iostream>
using namespace std;

// Component 클래스의 선언
class Component
{
  private:
    string text;
    public:
    Component(string text);
    void draw();
};
// Decorator 클래스의 선언
class Decorator
{
  private:
    Component component;
  public:
    Decorator(Component component);
    void draw();
};
#endif

구현 파일(decorator.cpp)

/**************************************************************
 * 데코레이터 패턴 예제의 구현 파일                           *
 **************************************************************/
#include "decorator.h"

// Component의 생성자
Component::Component(string tx)
:text(tx)
{
}
// Component::draw()의 구현
void Component::draw()
{
  cout << text << endl;
}
// Decorator의 생성자
Decorator::Decorator(Component comp)
: component(comp)
{
}
// Decorator::draw()의 생성자
void Decorator::draw()
{
  cout << "*************" << endl;
  component.draw();
  cout << "*************" << endl;
}

애플리케이션 파일(app.cpp)

/**************************************************************
 * 데코레이터 패턴 예제를 사용하는 애플리케이션 파일          *
 **************************************************************/
#include "decorator.h"

int main()
{
  // 간단한 문장 만들고 테두리 둘러 출력
  Decorator decor1(Component("안녕하세요!"));
  decor1.draw();
  // 간단한 문장 만들고 테두리 둘러 출력
  Decorator decor2(Component("안녕하세요 여러분!"));
  decor2.draw();
  // 간단한 문장 만들고 테두리 둘러 출력
  Decorator decor3(Component("데코레이터 패턴 예제입니다!"));
  decor3.draw();
  return 0;
}

 

7. 컴포짓 패턴

여러 객체로 구성된 객체를 하나의 간단한 객체처럼 취급해야 할 때 컴포짓 패턴(composite pattern)을 사용합니다. 예를 들어 폴더와 파일의 예로 생각하면 쉽습니다. 폴더는 폴더 단독으로도 있을 수 있고, 다른 폴더 내부에 들어 있을 수도 있습니다.

Composite 객체와 Leaf 클래스는 모두 Component 클래스에서 상속됩니다. 그리고 Composite 객체는 여러 개의 Component 객체를 포함할 수 있습니다. 이때 다형성 관계로 인해 Composite 객체가 Leaf 클래스를 여러 개 가질 수 있습니다. 즉 간단하게 Leaf 객체를 만들어서 사용할 수 있고, Leaf 객체를 포함하는 Composite 객체를 만들어서 사용할 수도 있습니다.

예제 코드

더보기

인터페이스 파일(composite.h)

/**************************************************************
 * Figure, Point, Mulripoint 클래스의 인터페이스              *
 **************************************************************/
#ifndef COMPOSITE_H
#define COMPOSITE_H
#include <iostream>
#include <utility>
#include <vector>
using namespace std;

// Figure 클래스의 인터페이스
class Figure  
{
  public: 
  virtual void show() = 0;
};
// Point 클래스의 인터페이스
class Point : public Figure  
{
  private:
    pair<double, double> point;
  public: 
    Point(double x, double y);
    void show();
};
// Multipoint 클래스의 인터페이스
class Multipoint : public Figure  
{
  private:
    int size;
    vector<Figure*> points;
  public:
    Multipoint();
    void addPoint(Figure* point); 
    void show();
};
#endif

구현 파일(composite.cpp)

/**************************************************************
 * Point와 Multipoint 클래스의 구현 파일                      *
 **************************************************************/
#include "composite.h"

// Point의 생성자
Point::Point(double x, double y)
{
  point.first = x;
  point.second = y;
}
// Point의 show 멤버 함수
void Point::show()
{
  cout << "(" << point.first << " , " << point.second << ")" << endl ;
}
// Multipoint의 생성자
Multipoint::Multipoint()
{
  size = 0;
}
// Multipoint의 addPoint 멤버 함수
void Multipoint::addPoint(Figure* point)
{
  points.push_back(point);
  size++;
}
// Multipoint의 show 멤버 함수
void Multipoint::show()
{
  for(int i = 0; i < size; i++)
  {
    points[i]->show();
  }    
}

애플리케이션 파일(app.cpp)

/**************************************************************
 * Point와 Multipoint 클래스를 사용하는 애플리케이션 파일     *
 **************************************************************/
#include "composite.h"

int main()
{
  // Point 객체(리프)만 만들고 출력
  cout << "Point 객체 만들고 출력하기" << endl;
  Point point(7.77, 2.24);  
  point.show();
  cout << endl;
  // Multipoint 객체(컴포짓)를 만들고 출력
  cout << "Multipoint 객체 만들고 출력하기" << endl;
  Multipoint multipoint;
  multipoint.addPoint(new Point(3.22, 4.51));
  multipoint.addPoint(new Point(4.12, 8.32));
  multipoint.addPoint(new Point(1.12, 7.44));
  multipoint.show();
  cout << endl;
  return 0;
}

 

 

지난 편에 이어서 디자인 패턴의 생성 패턴과 구조 패턴에 대해 알아보았습니다. 현재 티스토리에 올릴 때는 책에 있는 uml 다이어그램을 직접 만들어 올리는데, 행동 패턴은 uml이 너무 많아 노션에는 정리하였지만 따로 올리지는 않겠습니다. 20장(디자인 패턴)은 원서로  www.mhhe.com/forouzan1e 에서 볼 수 있습니다.

 

 

 

출처 : 포르잔 C++ 바이블

 

'내일배움캠프 > TIL' 카테고리의 다른 글

[내일배움캠프 Day17] C++ 빌드 과정  (1) 2025.01.09
[내일배움캠프 Day16] C++ 3주차 과제 진행  (1) 2025.01.09
[내일배움캠프 Day14] 디자인 패턴 - 생성 패턴  (1) 2025.01.06
[내일배움캠프 Day13] C++ 2주차 과제 진행  (1) 2025.01.03
[내일배움캠프 Day12] 반복자와 포인터  (2) 2025.01.02
'내일배움캠프/TIL' 카테고리의 다른 글
  • [내일배움캠프 Day17] C++ 빌드 과정
  • [내일배움캠프 Day16] C++ 3주차 과제 진행
  • [내일배움캠프 Day14] 디자인 패턴 - 생성 패턴
  • [내일배움캠프 Day13] C++ 2주차 과제 진행
개발자 밍
개발자 밍
dev0404 님의 블로그 입니다.
  • 개발자 밍
    Developer
    개발자 밍
  • 전체
    오늘
    어제
    • 분류 전체보기 (88)
      • 강의 (8)
        • UE Climbing System (3)
        • UE Dungeon (1)
        • HCI (4)
      • 책 (18)
        • 객체지향의 사실과 오해 (5)
        • Effective C++ (3)
        • 이득우의 게임 수학 (4)
        • 이것이 취업을 위한 컴퓨터 과학이다 (4)
        • 리뷰 (2)
      • C++ (2)
      • 알고리즘 (2)
      • 자료구조 (1)
      • Unreal (4)
      • 내일배움캠프 (52)
        • TIL (52)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    컴퓨터 구조
    컴퓨터구조
    내일배움캠프
    그래픽스
    객체지향
    자료구조
    게임수학
    c++
    언리얼
    Effective
    알고리즘
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
개발자 밍
[내일배움캠프 Day15] 디자인 패턴 - 구조 패턴
상단으로

티스토리툴바