[C++] Design Pattern
클래스와 객체의 관계를 어떻게 구성해야 할까?
이런 상황에서 클래스는 어떻게 구성해야 할까?
위와 같이 자주 발생하는 문제에 대한 정리된 해결책을 디자인 패턴이라고 한다.
디자인 패턴은 문제 해결의 방법론이고, Paradigm에 맞춰서 정의된다. (함수형 언어의 paradigm, 객체지향 언어의 paradigm)
몇 가지 디자인 패턴에 대해 알아보자.
Singleton
클래스가 하나의 인스턴스만을 가지도록 한다.
클래스의 생성자를 private 접근제어자로 정의하고, instance를 static 으로 정의해 어디서든지 해당 instance로 접근할 수 있되, 인스턴스의 생성은 단 한 번만 진행되도록 한다.
Builder
복잡한 객체를 한 단계씩 생성한다.
생성해야 하는 객체가 복잡해지면 생성자로 넘겨야 할 인자도 많아진다.
이 때 넘겨야 하는 인자의 순서, 개수를 항상 제대로 기억할 수 있을까?
Builder 패턴을 사용하면 이 문제를 해결할 수 있다.
예시를 통해 살펴보자.
House 객체를 만들기 위한 HouseBuilder 클래스를 정의한다.
HouseBuilder 객체에서는 House 클래스가 가지고 있는 멤버 변수들을 설정할 수 있는 Setter 메서드들을 가지고 있다.
사용자는 HouseBuilder 인스턴스를 만들고 필요한 멤버변수들을 Setter로 설정한 후 마지막에 Build() 메서드를 통해 House 객체를 반환받아서 사용한다.
House 객체를 생성자를 통해 만들어야 했을 때 보다 더 복잡하다고 생각할 수 있지만, 실수를 최대한 줄일 수 있다.
House 클래스에서 HouseBuilder를 친구 관계로 설정함을 확인할 수 있다.
덕분에 HouseBuilder는 House 객체의 private 멤버에도 접근할 수 있게 된다.
House 클래스의 윗부분에 class HouseBuilder; 를 정의하는 경우도 있는데, circular dependency를 피하기 위해서이다.
HouseBuilder 클래스를 선언 해 두면 생성자의 구조까지는 몰라도 클래스의 이름은 알 수 있어 circular dependency를 피할 수 있다.
자바에서는 IDE가 다 처리 해 줘서 circular dependency를 만날 일이 없다.
Prototype
객체를 복사할 때 사용한다.
외부에서는 private / protected 멤버 때문에 객체를 제대로 복사 할 수 없는 경우가 많다.
클래스 내부에 clone 함수를 정의하고 clone 함수를 통해 private 멤버까지 함께 복사한다.
clone 함수를 통해 객체를 복사함으로써 copy constructor의 호출을 방지할 수 있다.
factory
클래스의 인스턴스를 만드는 과정을 추상화 한 패턴이다.
factory를 설정하고 획일화된 객체를 생성할 때 사용한다.
인터페이스를 도입해 클래스를 여러 계층으로 분리한다.
Adapter
xml 형식의 데이터를 json 형식의 데이터로 활용해야 하는 경우를 생각해보자.
xml을 json 형식으로 다시 만드는 방법도 있겠지만, adapter 패턴을 도입하면 문제를 더 효과적으로 해결할 수 있다.
adapter 패턴은 특정 정보를 상호작용이 가능한 정보로 데이터로 변환하는 adapter를 데이터 사이에 추가하는 패턴이다.
굳이 패턴이라는 이름을 붙여야 할까?
그냥 함수 하나 추가하면 같은 작업을 할 수 있는데..
함수를 만들어서 사용해도 같은 결과를 얻을 수 있지만, 이 방법은 절차지향적이다.
객체지향적으로 문제를 해결하기 위해 adapter 패턴을 도입한다고 생각하자.
Bridge
객체를 구성할 때 다른 객체들과의 연결로 구성하는 패턴이다.
적당한 단위로 객체를 분리해서 여러 계층으로 나누어 정의한다.
추후 수정이 필요하거나 기능을 추가하는 등 변화에 효과적으로 대응할 수 있다.
Composite
Bridge 패턴과 유사하다.
객체를 구성할 때 트리 구조로 설계하는 패턴이다.
클래스를 트리 구조로 구성한다.
그림처럼 클래스를 구성한 후 박스 내부의 물건의 가격 합을 구하는 함수 등을 제작해 문제를 효과적으로 해결할 수 있다.
Facade
외부 라이브러리를 가져다가 사용하는 경우 해당 라이브러리가 업데이트되면 의존 관계에 놓인 모든 모듈들도 함께 업데이트 해야 한다.
외부 라이브러리를 많이 사용하는 경우 하나 하나 다 바꿔야 할텐데.. 외부 라이브러리를 직접 조작하는건 불가능하고..
이 때 Facade 패턴을 사용한다.
외부 라이브러리 전담 객체를 만들어 해당 객체만 외부 라이브러리에 의존하도록 하고, 원래 의존하던 클래스는 새로 만든 객체에 의존하도록 설정한다.
이렇게 추상화 시 유지 보수가 편해지고 의존성이 줄어든다는 장점이 있다.
Proxy
클라이언트와 서버가 멀리 떨어져 있는 경우 통신에 사용하는 비용이 증가한다.
이 때 프록시 패턴을 사용한다.
클라이언트와 실제 서버 사이에 프록시 서버를 두고 클라이언트는 프록시 서버와 통신하도록 설정한다.
일종의 캐시 값을 사용한다.
프록시에는 실제 서버로부터 받아온 값이 저장돼있고, 클라이언트는 프록시 서버의 캐시 값을 받아온다.
프록시 서버는 프로그래밍이 가능하다.
실제 서버가 업데이트될 경우 프록시의 캐시 값이 실제 서버의 데이터와 다른 경우가 생길 수 있으니 프록시를 주기적으로 업데이트 하는 방식을 사용한다.
언뜻 보면 Facade 패턴과 Proxy 패턴이 유사해 보인다.
Facade 패턴은 복잡한 외부 라이브러리를 전담 객체를 사용해 편하게 사용할 수 있도록 추상화해 의존성을 줄이고, Proxy 패턴은 기능을 추가하는 역할을 한다.
패턴들의 목적 자체가 다르니 설계 시 적합한 패턴을 사용하도록 하자.
'Programming Language > C++' 카테고리의 다른 글
[C++] Template (0) | 2022.12.17 |
---|---|
[C++] STL (0) | 2022.12.15 |
[C++] 정리 (1) (0) | 2022.11.23 |
[C++] OOP (5) Dynamic dispatch / Multiple Inheritance (0) | 2022.11.22 |
[C++] OOP (4) Type Casting (0) | 2022.10.19 |
댓글
이 글 공유하기
다른 글
-
[C++] Template
[C++] Template
2022.12.17 -
[C++] STL
[C++] STL
2022.12.15 -
[C++] 정리 (1)
[C++] 정리 (1)
2022.11.23 -
[C++] OOP (5) Dynamic dispatch / Multiple Inheritance
[C++] OOP (5) Dynamic dispatch / Multiple Inheritance
2022.11.22