카테고리:

디자인 패턴

소프트웨어 디자인 과정에서 자주 발생하는 문제들에 대한 해결책을 모아논 것을 디자인 패턴이라 합니다.

디자인 패턴은 생성, 구조, 행동, 총 3가지의 패턴으로 나뉘어져 있습니다.


생성

생성 패턴은 코드의 유연성과 재사용을 증가시키는 다양한 객체 생성 매커니즘을 위한 패턴입니다.

Factory Method

팩토리 메서드는 부모 클래스에서 객체들을 생성할 수 있는 인터페이스를 제공하고, 생성될 객체의 유형은 자식에서 정하는 생성 패턴입니다.

팩토리를 구현하는 방법은 추상 클래스 팩토리에 가상 함수로 객체를 생성하는 메서드를 작성한다.

이 후 생성할 객체의 종류가 추가될 수록 해당 클래스의 객체를 반환하는 팩토리를

위의 추상 클래스 팩토리를 상속 받아 만들어 모든 팩토리에 동일한 함수를 통해 객체를 생성되게 한다.

Abstract Factory

추상 팩토리는 팩토리 메서드와 비슷하게 객체를 생성하는 팩토리를 자식에서 정하여 생성하는 패턴입니다.

추상 팩토리가 팩토리 메서드와 다른 점은 한 개의 객체 종류만을 생성하는 팩토레 메서드와 다르게 추상 팩토리는 연관된 여러 종류의 객체를 생성한다는 점입니다.

Builder

빌더는 복잡한 객체를 단계별로 생성하여 같은 코드를 통해 다양한 유형을 가진 객체를 생성할 수 있게 하는 패턴입니다.

예를 들어 집을 객체로 표현할 때 집은 수영장, 층수, 마당, 담벼락 등 다양한 요소를 가집니다.

이러한 표현할 수 있는 모든 유형들을 bool 값으로 생성자로 받는다면 코드가 매우 더러워지고 이해하기 힘들 것 입니다.

이를 해결하기 위해서 빌더로서 집에 수영장, 층수 등 요소들을 추가하는 클래스를 만드는 것입니다.

Prototype

프로토타입은 복사하고 싶은 객체의 클래스에 의존하지 않고 객체를 복사하게 도와주는 패턴입니다.

객체를 복사하는 과정은 같은 클래스의 새 객체를 만들어 모든 값을 복사하면 되지만, 만약 일부 멤버들이 비공개여서 값을 확인할 수 없다면 이를 복사할 수 없습니다.

즉, 객체의 외부에서 객체를 복사하는 것은 항상 가능하지 않습니다.

프로토타입은 복사할 객체의 내부에 메서드를 구현하여 내부 멤버에 접근할 수 있도록 하여 복사하는 과정을 해당 메서드에게 위임하는 것 입니다.

Singleton

싱글턴이란 클래스에 하나의 인스턴스만 존재하게 하고 이 인스턴스에 대한 전역 접근을 허용하는 패턴입니다.

싱글턴은 실제로 디자인 패턴으로 보지 않는 경우도 많으며, 자주 사용되지 않는 경우가 많습니다.

왜냐면 싱글턴은 그 특성 때문에 SOLID 원칙 중 단일 책임 원칙을 위반하기 때문입니다.

싱글턴을 만들기 위해서는 생성자를 private으로 하여 한 프로세스에 한번만 호출하여 하나의 인스턴스만 생성하고 이를 반환하는 식으로 사용합니다.


구조

구조 패턴은 기존에 존재하던 구조를 유지하며 다양한 객체와 클래스를 더 큰 구조로 통합하여 관리하는 디자인 패턴들을 포함합니다.

Adapter

어댑터는 서로 호환되지 않는 인터페이스를 사용할 수 있도록 해결하는 디자인 패턴입니다.

Example: XML기반으로 개발된 앱에서 JSON기반 데이터베이스에 접근하는 경우? → 어댑터를 통해 호환시켜 해결한다.

게임 개발에서라면 오래된 레거시 코드를 보유하고 있는 게임 엔진에서 최신 환경으로 끌어다 사용하고 싶은 경우 어댑터를 통해서 해결할 수 있습니다.

Bridge

브리지는 거대한 클래스 또는 서로 밀접하게 연관된 클래스의 구현과 추상화를 개별적으로 개발할 수 있도록 하는 디자인 패턴입니다.

브리지가 가장 활용될 수 있는 부분은 크로스 플랫폼 환경입니다.

여러 다양한 플랫폼에서 활용되는 앱을 개발한다고 하면, 실제로 앱이 하는 구현 부분과, 추상화를 통해 다양한 플랫폼에서 작동되는 방식으로 나눈다면 모든 플랫폼마다 구현을 작성하고 수정 사항이 생길 시

추상화된 부분의 변경만을 통해 해결이 가능합니다.

Composite

복합체는 다양한 객체들을 트리 형태로 나타내어 독립적으로 적용될 수 있게 하는 디자인 패턴입니다.

트리 형태로 나타내어야 하기 때문에, 객체들의 연관 관계가 트리 형태로 이루어져야 사용이 가능한 디자인 패턴입니다.

계층 구조, 그래프 등의 구조에서 사용되며, 모두 하나의 클래스를 기반으로 하기 때문에 동일한 명령으로 다양한 작업을 할 수 있습니다.

Decorator

데코레이터는 어떠한 객체를 감싸 새로운 기능을 추가하고 싶을 때 사용되는 디자인 패턴입니다.

예를 들어, Base 클래스에 Work라는 함수가 존재하며 이를 상속받는 A라는 클래스가 있습니다.

A라는 클래스의 Work에 추가적인 기능을 부여하고 싶은 경우, 이를 상속이나 클래스에 추가적인 구현을 통해 해결할 수 있지만, 이는 정적인 해결법으로 런타임 도중에는 불가능합니다.

이를 해결하기 위해 동일한 Base 클래스를 상속받는 Decorator 클래스로 집약관계를 통해 A의 Work를 실행하기 전 추가적인 작업을 진행할 수 있습니다.

Facade

퍼사드는 복잡한 클래스, 프레임워크에 대한 단순화된 인터페이스를 제공해주는 디자인 패턴입니다.

퍼사드는 데코레이터와 비슷하게 여러 클래스를 집약관계로 포함하고 있습니다.

집약관계로 포함 후 단순화된 여러 함수를 사용자에게 제공하며, 이 함수 내부에서 포함한 클래스들을 동작시키는 과정을 통해 외부에서 볼때는 간단하지만 내부에서 모든 과정을 처리해주는 방법을 사용합니다.

Flyweight

플라이웨이트는 메모리 절약을 위한 디자인 패턴으로 공통으로 사용되는 객체들에 대한 소유권을 나눠 각자 소유하지 않고 공유합니다.

총알을 사용하는 게임을 예로 든다면, 모든 총알은 하나의 객체이며 이미지, 좌표, 속도 등의 변수를 갖고 있습니다.

여기서 이미지는 많은 메모리를 잡아먹기 때문에, 총알 객체가 이미지를 소유한다면 수많은 총알 객체를 생성하며 많은 메모리를 잡아먹기 때문에 메모리 문제가 발생합니다.

플라이웨이트는 이러한 이미지 객체를 따로 두어 공유하여 사용하며 메모리 소모를 줄이는 방법입니다.

Proxy

프록시는 어느 특정 개체에 대한 접근을 다른 클래스에서 모두 관리하는 디자인 패턴입니다.

프록시는 실제로는 잘 사용되지 않으며, 무거운 객체에 접근하는 것을 프록시를 통해 관리하는 경우가 종종 있습니다.

프록시를 통해 해당 객체 내부 구현 내용을 변경하지 않고 작동 방식을 제어하거나 바꿀 수 있습니다.


행동

행동 패턴은 객체의 책임과 알고리즘 등에 연관되어 있는 디자인 패턴들을 일컫습니다.

Chain of Responsibility

책임 연쇄 패턴은 연쇄적으로 연결되어 있는 핸들러에 요청을 보냈을 때, 각 핸들러의 설정에 따라 요청을 진행할지 또는 다음 핸들러에게 요청을 넘기는 패턴입니다.

책임 연쇄 패턴은 여러개의 필터가 필요한 내용이나, 이벤트 체인 등에 사용됩니다.

Command

커맨드 패턴은 별개의 객체에게 요청과 요청에 필요한 모든 데이터를 보내 처리하는 행동 패턴입니다.

커맨드 패턴은 UI 등 기능 구현에 사용됩니다.

예를 들어 사용자의 UI에 존재하는 버튼들의 기능을 각 객체가 보유하지 않고, 해당 버튼을 클릭하면 일을 처리해주는 다른 객체에게 요청과 요청에 필요한 데이터를 보내어 작업을 처리하는 과정입니다.

이는 UI가 너무 큰 데이터를 보유하지 않고, 이를 처리하는 기능을 한군데에 묶을 수 있는 이점을 가질 수 있습니다.

Iterator

이터레이터(반복자) 패턴은 데이터의 구성 요소들을 데이터의 구조를 드러내지 않고 순회할 수 있도록 해주는 행동 패턴입니다.

데이터를 저장하는 구조는 트리, 큐, 배열 등 다양하게 존재하며, 이를 순회하는 방법은 서로 다르기 때문에 구조를 알고 다른 방법을 사용하여야 합니다.

하지만 이터레이터를 통해 데이터의 구조를 알지 못해도 다양한 방법을 사용하여 원소들을 순회하게 할 수 있습니다.

이터레이터는 실제로 C++에서 매우 자주 사용되는 행동 패턴 중 하나로, iterator를 통해 벡터, 큐, 스택 등 다양한 구조의 원소에 접근하여 문제 해결에 사용할 수 있습니다.

다양한 자료 타입에 사용되기 때문에 템플릿으로 구현되는 경우가 많고, 포인터를 활용하여 현재, 이전, 다음, 끝, 처음 등의 원소를 빠르게 접근할 수 있습니다.

Mediator

중재자 패턴은 여러 객체의 의존 관계가 난잡해지지 않게 하나의 객체를 통해 상호작용하도록 제한하는 행동 패턴입니다.

중재자 패턴은 그래픽 인터페이스 설계에 가장 자주 사용됩니다.

예를 들어 다양한 UI들이 서로 소통하는 그래픽 인터페이스들은, 서로 간의 소통이 많기 때문에 이를 하나의 클래스를 두어 소통을 관리하는 방식을 사용합니다.

Memento

메멘토 패턴은 객체의 구현 사항을 외부에 공개하지 않고 객체 상태를 저장하고 복원하는 기능을 제공하는 행동 패턴입니다.

메멘토는 직렬화를 통해 구현할 수 있으며, 직렬화 된 메타데이터를 외부 클래스에서 참조하여 저장하고 사용하는 방식을 사용합니다.

State

상태 패턴은 흔히 보는 다양한 상태에 따른 행동을 제어하기 위한 행동 패턴입니다.

기존에 어떠한 객체의 상태에 따른 행동은 Switch문으로 구현되었지만, 이는 코드가 방대해질수록 유지보수가 힘들어진다는 단점을 갖습니다.

이를 해결하기 위한 것이 상태 패턴으로, 상태 패턴은 이러한 행동들을 객체화하여 방대한 코드를 분할하고 관리하기 쉽게 만듭니다.

Strategy

전략 패턴은 알고리즘의 카테고리를 정하여 객체들을 하나로 묶어 서로 상호작용 할 수 있도록 하는 행동 패턴입니다.

전략 패턴은 상태 패턴과 흡사하게 주어진 다양한 요구사항에 따라 다양한 답을 주어야합니다.

예로, 어느 지점까지의 가는 경로가 자동차, 도보, 자전거 등 다양하게 존재할 때, 이들을 객체화 하여 하나의 카테고리로 묶어 활용하는 것입니다.

Template Method

템플릿 메서드는 하나의 구조를 여러 다른 클래스로 활용하게 하는 행동 패턴입니다.

부모 클래스에서 구조를 확립시킨 후, 필요한 요소들만 재정의를 통해 구현하는 것이 템플릿 메서드입니다.

Visitor

비지터 패턴은 객체와 객체가 사용하는 알고리즘을 분리하는 행동 패턴입니다.

비지터 패턴을 활용하여 둘을 분리하지 않는다면 알고리즘에 변경사항이 생길 때 마다 모든 객체에 대해 진행해주어야 하며, 이는 유지보수에 큰 걸림돌이 될 수 있습니다.

그리하여 visitor 패턴을 통해 객체에서 알고리즘으로 접근하는 단계에 하나의 단계를 추가하여 마치 커맨드 패턴과 유사하게 작동시킵니다.

참조: https://refactoring.guru/ko