티스토리 뷰
목차
1. GoF
- Design Patterns -> Elements of Reusable Object-Oriented Software를 집필한 저자 4명을 말함
- 에릭감마(Erich Gamma)
- 리차드 헬름(Richard Helm)
- 랄프 존슨(Ralph Johnson)
- 존 블리시데스(John Vlissides)
2. 디자인 패턴
- 모듈의 세분화된 역할이나 모듈들 간의 인터페이스 구현 방식을 설계할때 참조할 수 있는 방식
- 소프트웨어를 설계할 때 자주 발생하는 문제들에 대한 재사용 가능한 해결책
- 디자인 패턴은 세 가지 카테고리로 분류되어 23개의 패턴이 있음
- 디자인 패턴 사용의 이점
- 디자인 패턴 사용시 검증된 해결책을 제공하므로, 같은 문제를 반복해서 해결할 필요 없이 패턴을 적용하여 빠르게 개발 가능
- 패턴을 사용하면 코드 변경이 용이하고 새로운 기능을 쉽게 추가할 수 있음
- 코드가 체계적으로 정리되어 가독성이 높아지고 팀원 간 협업이 쉬워짐
- 디자인 패턴은 SOLID 원칙과 같은 객체 지향 설계를 따르도록 도와줌
2-1 생성 패턴 (Creational Pattern)
: 객체 인스턴스를 생성하는 패턴으로, 클라이언트와 그 클라이언트가 생성해야 하는 객체 인스턴스 사이의 연결을 끊어주는 패턴
□ 특징
■ 생성패턴은 시스템이 어떤 구체 클래스를 사용하는지에 대한 정보를 캡슐화
■ 생성패턴은 이들 클래스의 인스턴스들이 어떻게 만들고 어떻게 서로 맞붙는지에 대한 부분을 완전히 가림
- 싱글톤 패턴 (Singleton Pattern)
- 인스턴스 하나만을 만들어 사용하기 위한 패턴
- 커넥션 풀, 스레드 풀, 디바이스 설정 객체 등의 경우 인스턴스를 여러 개 만들게 되면 자원을 낭비하게 되거나 버그를 발생시킬 수 있으므로 오직 하나만 생성하고 그 인스턴스를 사용하도록 하는 것이 싱글톤 패턴의 목적
- 하나의 인스턴스만을 유지하기 위해 인스턴스 생성에 특별한 제약을 걸어둬야 함
- new를 실행할 수 없도록 private 접근 제어자를 지정하고, 유일한 단일 객체를 반환할 수 있도록 정적 메소드를 지원해야 함, 또한 유일한 단일 객체를 참조할 정적 참조변수가 필요함
- 추상 팩토리 패턴 (Abstract Factory Pattern)
- 비슷한 속성의 객체들을 인터페이스로 규격화된 팩토리에서 일괄된 방식으로 생성하고, 생성된 객체끼리는 쉽게 교체될수 있도록 고안된 패턴
- 상세화된 서브클래스를 정의하지 않고도 서로 관련성이 있거나 독립적인 여러 객체의 군을 생성하기 위한 인터페이스 제공
- 팩토리 메소드 패턴 (Factory method Pattern)
- 객체를 생성하기 위해 인터페이스를 정의하지만 어떤 클래스의 인스턴스를 생성할 지에 대한 결정은 서브클래스가 내리도록 할 때 유용하게 사용됨
- 어떤 클래스가 자신이 생성해야 하는 객체의 클래스를 예측할 수 없을 때 사용함
- 빌더 패턴 (Builder Pattern)
- 복잡한 객체를 생성하는 방법을 정의하는 클래스와 표현하는 방법을 정의하는 클래스를 별도로 분리하여 서로 다른 표현이라도 이를 생성할 수 있는 동일한 절차를 제공하는 패턴
- 빌더 패턴은 많은 Optional한 멤버 변수나 지속성 없는 상태 값들에 대해 처리해야 하는 문제들을 해결함
- Builder 클래스를 만들어 필수 값에 대해서는 생성자를 통해, 선택적인 값들에 대해서는 메소드를 통해 step-by-step으로 값을 입력받은 후에 build() 메소드를 통해 최종적으로 하나의 인스턴스를 리턴하는 방식
- 빌더 패턴은 굉장히 자주 사용되는 생성 패턴 중 하나로 Retrofit이나 Okhttp등 유명 오픈소스에서도 이 빌더 패턴을 사용하고 있음
- 프로토 타임 패턴 (Prototype Pattern)
- 프로토타입은 주로 실제 제품을 만들기에 앞서 대략적인 샘플 정도의 의미로 사용되는 단어
- 생성할 객체들의 타입이 프로토타입인 인스턴스로부터 결정되도록 하며 인스턴스는 새 객체를 만들기 위해 자신을 복제하게 됨
- 패턴을 구현하려면 우선 clone() 메소드를 선언하는 추상 클래스를 하나 만듬
- 다형적 생성자(polymorphic constructor) 기능이 필요한 클래스가 있다면 위의 추상 클래스를 상속받게 한 후, clone() 메소드 내의 코드를 구현함
- 추상 팩토리 패턴과는 반대로 클라이언트 응용 프로그램 코드 내에서 객체 창조자 서브 클래스하는 것을 피할 수 있게 해줌
- 새로운 객체는 일반적인 방법으로 객체를 생성하는 고유의 비용이 주어진 응용 프로그램 상황에 있어서 불가피하게 매우 클 때, 이 비용을 감내하지 않을 수 있게 해줌
- 객체를 복사하여 생성하는 방식이므로 다수 객체를 생성해야 할 경우 객체 생성의 비용을 효과적으로 감소시킬 수 있음, 프로토 타입이 존재하고 복제만 해서 객체를 생성하게 되므로 서브 클래스의 수를 줄일 수 있다는 장점도 있음
2-2 구조 패턴 (Structural Pattern)
: 구조 패턴은 클래스난 객체를 조합해 더 큰 구조를 만드는 패턴, 서로 다른 인터페이스를 지닌 2개의 객체를 묶어 단일 인터페이스를 제공하거나 객체들을 서로 묶어 새로운 기능을 제공하는 패턴
□ 특징
■ 서로 독립적으로 개발한 클래스 라이브러리를 마치 하나인 것처럼 사용할 수 있음
■ 여러 인터페이스를 합성하여 서로 다른 인터페이스들의 통일된 추상을 제공
■ 인터페이스나 구현을 복합하는 것이 아니라 객체를 합성하는 방법을 제공
- 어댑터 패턴 (Adapter Pattern)
- 기존에 있는 시스템에 새로운 써드파티 라이브러리가 추가된다던지, 레거시 인터페이스를 새로운 인터페이스로 교체하는 경우에 코드의 재사용성을 높일 수 있는 방법
- 호환성이 없는 인터페이스 때문에 함께 동작할 수 없는 클래스들이 함께 작동하도록 해주는 패턴
- 관계가 없는 인터페이스 간 같이 사용할 수 있고 프로그램 검사에 용이하며 클래스 재활용성이 증가됨
- 브리지 패턴 (Bridge Pattern)
- 구현부에서 추상층을 분리하여 각자 독립적으로 변형할 수 있게 하는 패턴, 추상적 개념과 구체적 구현을 서로 다른 두개의 인터페이스로 구현하는 디자인 패턴
- 브리지 패턴은 캡슐화, 집합을 사용하고 또한 다른 클래스들로 책임을 분리시키기 위해 상속을 사용할 수 있음
- 인터페이스와 구현이 분리되고, 서로 독립적으로 확장할 수 있음
- 수현 세부사항을 클라이언트에게 은닉하여 캡슐화를 지킬 수 있음
- 합성 패턴 (Composite Pattern)
- 객체들의 관계를 트리 구조로 구성하여 전체-부분 계층을 표현하는 패턴으로 여러개의 객체들로 구성된 복합 객체와 단일 객체를 클라이언트에서 구별 없이 다루게 함
- 전체-부분의 관계를 갖는 객체들 사이의 관계를 정의할 때 유용함 또한, 클라이언트는 전체와 부분을 구분하지 않고 동일한 인터페이스를 사용할 수 있음
- 데코레이터 패턴 (Decorator Pattern)
- 객체의 결합을 통해 기능을 동적으로 유연하게 학장할 수 있게 해주는 패턴
- 객체에 추가적인 요건을 동적으로 첨가하며 기능 확장이 필요할 때 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있음
- 기본 기능에 추가할 수 있는 기능의 종류가 많은 경우에 각 추가 기능을 Decorator 클래스로 정의한 후 필요한 Decorator 객체를 조합함으로써 추가 기능의 조합을 설계하는 방식
- 퍼사드 패턴 (Facade Pattern)
- Facade(외관)는 "건물의 정면"을 의미하는 단어로 어떤 소프트웨어의 다른 커다란 코드 부분에 대하여 간략화된 인터페이스를 제공해주는 디자인 패턴을 의미
- 퍼사드 객체는 복잡한 소프트웨어 바깥쪽의 코드가 라이브러리의 안쪽 코드에 의존하는 일을 감소시켜 주고 복잡한 소프트웨어를 사용할 수 있게 간단한 인터페이스를 제공
- 소프트웨어 라이브러리를 쉽게 사용할 수 있게 해주고 쉽게 이해할 수 있게 해줌
- 공통적인 작업에 대해 간편한 메소드들을 제공
- 라이브러리 바깥쪽의 코드가 라이브러리의 안쪽 코드에 의존하는 일을 감소, 시스템을 개발하는 데 있어 유연성이 향상
- 플라이웨이트 패턴 (flyweight Pattern)
- 어떤 클래스의 인스턴스 한 개만 가지고 여러 개의 "가상 인스턴스"를 제공하고 싶을 때 사용하는 패턴
- 인스턴스를 가능한 대로 공유 시켜 쓸데없이 new 연산자를 통한 메모리 낭비를 줄이는 방식
- 주로 생성 된 객체 수를 줄이고 메모리 사용 공간을 줄이며 성능을 향상시키는 데 사용되며 이러한 유형의 디자인 패턴은 오브젝트 패턴을 감소시켜 어플리케이션에 필요한 오브젝트 구조를 향상시킴
- 프록시 패턴 (Proxy Pattern)
- 실제 기능을 수행하는 객체 대신 가상의 객체를 사용해 로직의 흐름을 제어하는 디자인 패턴
- 프록시 패턴을 사용하는 경우는 어떤 클래스의 객체 생성이 오래 걸리는 경우 그 일을 분업을 하여 proxy 클래스에서 처리 할 수 있는 부분은 처리를 하고 proxy 클래스에서 처리 할 수 없는 작업에 대해서만 실제 클래스의 객체를 생성하고 위임한느 방식을 취함
- RealSubject가 원격 시스템에 돌아가거나 그 객체의 생성 비용이 많이 들어 실제 사용 시점에 객체를 생성하거나 실제 객체에 접근을 제한 및 제어를 해야 할 때 등의 경우에 사용
- 원래 하려던 기능을 수행하며 그외의 부가적인 작업을 수행할 수 있음
- 비용이 많이 드는 연산을 실제로 필요한 시점에 수행할 수 있음
- 실제 객체의 리소스가 무거운 경우 프록시 객체에서 간단한 처리를 하거나 기본 객체를 캐싱 처리함으로써 부하를 줄일 수 있음
- 실제 객체에 대한 수정 없이 클라이언트에서의 사용과 기본 객체 사이에 일련의 로직을 프록시 객체를 통해 넣을 수 있음
- 프록시는 기본 객체와 요청 사이에 있기 때문에 일종의 방패의 역할도 함
- 사용자 입장에서는 프록시 객체나 실제 객체나 사용법이 유사하므로 구조나 코드 구현이 간단함
- 프록시 패턴 종류
- 원격 프록시 : 원격 객체에 대한 접근 제어가 가능함
- 가상 프록시 (Virtual Proxy) : 객체의 생성비용이 많이 들어 미리 생성하기 힘든 객체에 대한 접근 및 생성시점등을 제어
- 보호 프록시 (Protection Proxy) : 객체에 따른 접근 권한을 제어해야하는 객체에 대한 접근을 제어
- 벙화벽 프록시 (Firewall Proxy) : 일련의 네트워크 자원에 대한 접근을 제어함으로써 주 객체를 '나쁜' 클라이언트로부터 보호
- 스마트 레퍼런스 프록시 (Smart Reference Proxy) : 주 객체가 참조될 때마다 추가 행동을 제공
- 캐싱 프록시 (Caching Proxy) : 비용이 많이 드는 작업의 결과를 임시로 저장 하고, 추후 여러 클라이언트에 저장된 결과를 실제 작업처리 대신 보여주고 자원을 절약
- 동기화 프록시 (Synchronization Proxy) : 여러 스레드에서 주 객체에 접근하는 경우에 안전하게 작업을 처리, 주로 분산 환경에서 일련의 객체에 대한 동기화 된 접근을 제어해주는 자바 인터페이스에서 사용됨
- 복잡도 숨기 프록시 (Complexity Hiding Proxy) : 복잡한 클래스들의 집합에 대한 접근을 제어하고, 복잡도를 숨김
- 지연 복사 프록시 (Copy-On-Write Proxy) : 클라이언트에서 필요로 할 때까지 객체가 복사되는 것을 지연시킴으로써 객체의 복사를 제어함
2-3 행동 패턴 (Behavioral Pattern)
: 객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴, 한 객체가 수행할 수 없는 작업을 여러 개의 객체로 어떻게 분배하며 객체 사이의 결합도 최소화에 중점을 둠.
□ 특징
■ 복잡한 객체 간 통신을 간소화 할 수 있음
■ 각 객체의 독립성을 유지할 수 있고 목적 객체 간의 상호작용과 책임 분배를 구조화 할 수 있음
- 책임연쇄 패턴 (Chain of responsibility)
- 클라이언트 요청을 처리할 수 있는 처리객체를 집합으로 만들어 부여함으로 결합을 느슨하게 하기 위해 만들어진 디자인 패턴
- 여러개의 객체 중에서 어떤 것이 요구를 처리할 수 있는지 사전에 알 수 없을 때 사용
- 어떤 요청이 들어왔을 때 그것을 수신하는 객체가 자신이 처리할 수 없는 경우에는 다음 객체에게 문제를 넘김으로써 최종적으로 요청을 처리 할 수 있는 객체에 의해 처리가 가능하도록 하는 패턴
- 요청을 처리할 수 있는 객체가 여러개이고 처리 객체가 특정정이지 않을 경우 권장되는 패턴
- 책임연쇄 패턴의 이점
- 결합도를 낮추며, 요청의 발신자와 수신자를 분리시킬 수 있음
- 클라이언트는 처리객체의 집합 내부의 구조를 알 필요가 없음
- 집합 내의 처리 순서를 변경하거나 처리 객체를 추가 또는 삭제할 수 있어 유연성이 향상 됨
- 새로운 요청에 대한 처리객체 생성이 매우 편리함
- 책임연쇄 패턴의 단점
- 충분한 검즘을 거치지 않을 경우 집합 내부에서 무한 루프가 발생할 수 있음
- 디버깅 및 구조 파악이 쉽지 않음
- 커맨드 패턴 (Command Pattern)
- 요청에 따라야하는 기능들을 캡슐화한 객체에 정리하여 실행할 수 있게 해주는 디자인 패턴
- 요청에 따르는 기능들이 다양하고 변경 및 추가 삭제가 많은 경우 요청이 발생되는 클래스를 변경하지 않고 수정할 때 매우 유용
- 커맨드 패턴이 사용되는 경우
- 병렬처리(Parallel Processing) : 병렬로 여러 스레드에서 실행이 되어야 하는 경우
- 매크로(Macro) : 특정 명령에 따른 동일한 일련의 작업을 반복적으로 수행해야 하는 경우
- 네트워킹(Networking) : 네트워크를 통해 일련의 작업을 보내야하는 경우
- 커맨트 패턴 이점
- 코드를 변경하지 않고 새 명령을 추가할 수 있어 코드확장이 용이
- 호출자와 수신자의 결합도를 낮출 수 있음
- 인터프리터 패턴 ( Interpreter Pattern)
- 언어 문법이나 표현을 평가할 수 있는 방법을 제공
- 특정 컨텍스트를 해석하도록 지시하는 표현 인터페이스를 구현하는 것을 포함
- 이터레이터 패턴 (Iterator Pattern)
- 어떠한 객체의 집합을 순서대로 명령을 처리할 수 있게 해주는 패턴
- 컬렉션 구현 방법을 노출시키지 않으면서 그 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 방법을 제공해 주는 패턴으로 간단하면서 실제로도 많이 쓰이는 패턴
- 옵저버 패턴 (Observer Pattern)
- 객체의 상태 변화를 관찰하는 관찰자 객체를 생성하여 사용하는 디자인 패턴
- 객체의 변화가 발생하면 그에 따르는 종속 객체들이 자동으로 변화가 통지되어 그에 따른 명령을 수행하도록 1:N의 의존성을 정의
- 데이터의 변경이 발생했을 경우 상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 유용
- 옵저버 패턴을 사용하는 경우
- 분산 이벤트 핸들링 시스템
- 이벤트 기반 프로그래밍 (Android OS가 특정 이벤트를 감지하고 반응하는 구조라 옵저버 패턴을 많이 따름)
- 옵저버 패턴 이점
- 객체간의 결합도가 느슨해짐
- 실시간으로 데이터 배분을 효과적으로 할 수 있음
- 전략 패턴 (Strategy Pattern)
- 행위를 클래스로 캡슐화해 동적으로 행위를 자유롭게 바꿀 수 있게 해주는 패턴으로 같은 문제를 해결하는 여러 알고리즘이 클래스별로 캡슐화되어 있고 이들이 필요할 때 교체할 수 있도록 함으로써 동일한 문제를 다른 알고리즘으로 해결할 수 있게 하는 디자인 패턴
- 게임 프로그래밍에서 캐릭터가 자신이 처한 상황에 따라 공격이나 행동하는 방식을 바꾸고 싶을 때 스트레터지 패턴은 매우 유용
- 전략 패턴 이점
- 시스템의 구조 및 Context Class를 변경하지 않고 요청에 맞는 로직을 추가 및 수정할 수 있음
- 같은 인터페이스 양식을 가진 알고리즘을 별도로 캡슐화하여 코드의 가독성이 높아지며, 생산성이 높아짐
- 요청에 맞는 로직을 실시간으로 변경 가능
- 전략 패턴 이점
- 템플릿 메서드 패턴 (Template Method Pattern)
- 상위 클래스에서 처리의 흐름을 제어하며 하위 클래스에서 처리의 내용을 구체화 하는 디자인 패턴
- 공통되는 사항은 상위 추상 클래스에서 구현하며 각 객체마다 다른 부분은 하위 클래스에서 구현
- 상속을 통한 확장 개발 방법으로 코드의 중복을 줄이고 리팩토링에 유리하여 가장 많이 사용되는 패턴
- 방문자 패턴 (Visitor Pattern)
- 실제 로직을 가지고 있는 객체가 로직을 적용할 객체를 방문하면서 실행하는 패턴
- 로직과 구조를 분리하는 패턴으로 둘의 구조가 분리되면 구조를 수정하지 않고도 새로운 동작을 기존 객체 구조에 추가 할 수 있음
- 비슷한 종류의 객체들을 가진 그룹에서 작업을 수행해야 할 때 주로 사용되는 패턴
- 중재자 패턴 (Mediator Pattern)
- 객체들 간의 상호작용 행위를 정리하여 모은 중재자 객체를 따로 두어 관리하는 디자인 패턴
- 모든 클래스간의 복잡한 로직을 캡슐화하여 하나의 클래스에 위임하여 처리하는 패턴
- M:N의 관계에서 M:1의 관계로 복잡도를 떨어뜨려 유지 보수 및 재사용의 확정성에 유리한 패턴
- 커뮤니케이션을 하고자 하는 객체가 있을 때 서로가 커뮤니케이션 하기 복잡한 경우 이를 해결해주고 서로 간 쉽게 해주며 커플링을 약회시켜주는 패턴
- 중재자 패턴 이점
- 객체들 간 수정을 하지 않고도 관계를 수정할 수 있음
- 객체들 간의 관계의 복잡도, 의존성 및 결합도를 감소 시킴
- 상태 패턴 (State Pattern)
- 객체 내부의 상태에 따라 동작을 변경해야 할 때 사용하는 디자인 패턴
- 객체의 특정 상태를 클래스로 선언하고 클래스에서는 해당 상태에서 할 수 있는 행위들을 메서드로 정의
- 각 상태 클래스들을 인터페이스로 캡슐화하여 클라이언트에서 인터페이스를 호출
- 상태 패턴 이점
- 하나의 객체에 대한 여러 동작을 구현해야할 때 상태 객체만 수정하므로 동작의 추가, 삭제 및 수정이 간단해짐
- 상태 패턴을 사용하면 객체의 상태에 따른 조건문이 줄어들어 코드가 간결해지고 가독성이 향상 됨
- 기념품 패턴 (Memento Pattern)
- 객체의 상태 정보를 가지는 클래스를 따로 생성하여 객체의 상태를 저장하거나 이전 상태로 복원할 수 있게 해주는 패턴
- 바둑, 오목, 체스 등의 보드게임등에서 '무르기' 기능을 구현할 때 사용됨