객체지향 설계와 패턴 Lecture #8. 책임 패턴 2
- Proxy Pattern - 하나의 객체가 다른 객체를 대신하게 함
- Chain of Responsibility Pattern - 요청을 보내는 쪽과 받는 쪽의 결합을 피함
- Flyweight Pattern - 본질적인 것과 부가적인 것을 구분함
프록시 패턴
책임 체인 패턴
-
요약
- 요청을 보내는 객체(sender)와 요청을 처리하는 객체(receiver)를 분리
- 요청에 대하여 자신이 처리할 수 있으면 요청을 처리하고 처리할 수 없으면 연결된 다음 객체에 요청을 넘긴다.
-
역할
- 요청을 보내는 객체와 요청을 처리하는 객체를 분리하여 결합도를 줄여 추가, 수정시 코드 변경 영향을 줄인다.
- 결합도를 낮춤: 요청을 보내는 쪽은 어떤 객체에 전달할지 고려 안해도 되고 요청을 처리할 수 있는 객체들의 reference를 모두 가질 필요가 없다. 첫번 째 객체에게 요청을 보내면 그 다음부터는 연결고리 객체들이 자동으로 요청을 전파함 → 따라서 결합도를 낮출 수 있음
- 객체에 책임 배정이 융통적임: 연결고리 객체들을 동적으로 삽입, 삭제할 수 있음 → 수정, 변경 융통적, 객체들 간의 책임 분산이 동적으로 이루어짐
-
기존 설계(예제: 도움말GUI)
- 도움말 기능을 전담하는 객체 정의 → coupling이 높음(switch문) → 수정 및 기능 추가시 코드 변경이 많이 됨
- 장점: 도움말 관련 기능을 한 군데로 모임 → 관리와 기능 확장이 용이
- 단점: setState()함수를 이용하여 사용자의 현재 상태를 설정 → coupling 높음 → 새로운 상태와 기능이 추가 혹은 변경시 영향받는 범위 커짐.
-
Chain of Responsibility Pattern 적용한 설계(예제: 도움말GUI)
- 클래스 다이어그램
- 특정 요청에 대하여 이를 처리하는 객체를 고정하지 않고 요청을 처리하는 객체 group에 이 요청을 보냄
- 요청을 보내는 객체(sender)와 요청을 처리하는 객체(receiver)를 분리
- 객체들의 연결 고리(chain) → 요청에 대하여 각 객체는 자신이 처리할 수 있으면 처리 → 처리할 수 없으면 연결된 다음 객체에 요청을 넘김(chain of responsibility)
-
구조 및 구성요소
-
구현
- 후속 체인의 구현 → 기존에 존재하는 링크를 이용: 상속 관계로 인한 연결 레퍼런스 존재, 이 링크를 "책임 체인"링크로 사용할 수 있음 → 체인을 구성하기 위한 링크를 새로 정의: 일반적으로 핸들러 클래스에서 구현
- 다음 처리(successor) 연결 → 링크를 새로 정의하는 경우, 링크에 대한 관리와 호출을 구현해야 함
- 요청이 하나가 아니고 여러개라면 → 각 요청에 대하여 개별적인 함수 구현 → 요청을 표현하는 객체(Request)를 만들어서 이를 handleRequest()함수의 인자로 넘김
플라이웨이트 패턴
-
요약
- 수많은 작은 객체를 생성할 때 사용되는 많은 객체의 생성을 관리하는 객체를 따로 두어 이를 통해 필요한 객체를 참조형태로 사용한다.
- 본질적인 것과 부가적인 것을 구분하려 할 때 사용
-
역할
- 클래스가 데이터를 무겁게 갖고있을 때 공유할 필요가 있는 것과 없는 것을 나누어 공유할 수 있는 것을 분리하여 따로 관리하며 필요할 때 마다 참조해서 사용하도록 한다.
-
기존 설계(예제: 게임 캐릭터)
- 클래스다이어그램
-
Flyweight Pattern 적용한 설계(예제: 게임 캐릭터)
-
공유객체 관리
- 싱글톤 패턴 사용하기
공유 객체는 하나만 있어도 되므로 static으로 선언. 싱글톤 패턴을 사용하여 공유 객체는 하나만 생기도록 한다.
- 팩토리 사용하기
공유 서브클래스 객체를 생성하는 팩토리 클래스 생성 → 객체 생성 위임
각 객체는 자신에 맞는 공유 객체 생성을 팩토리에 요청
팩토리는 각 공유 서브 클래스마다 단 하나의 객체만 생성되도록 보장
→ 팩토리 패턴을 적용하여 공유 객체 생성을 위임 → 만들어져있는 공유 객체가 있으면 반환하고 없으면 새로 만들어서 반환
-
구조 및 구성요소
- 클래스다이어그램
- Flyweight: 공통 인터페이스. extrinsic data를 넘겨받아 연산 수행.
- ConcreteFlyweight: 공유될 수 있는 객체들. Flyweight 인터페이스를 구현하고 intrinsic data를 보관
- UnsharedConcreteFlyweight: 공유하지 않지만 공유되는 객체들과 같이 취급되는 객체들을 표현
- FlyweightFactory: flyweight객체를 생성하고 관리. 공유를 책임짐. 싱글톤 패턴 적용하여 객체 하나만 만들어지도록(공유 객체의 유일성 보장)
- Client: (필요하다면) extrinsic data를 보관하거나 계산한다. → Client는 직접적으로 ConcreteFlyweight 객체를 생성하지 않고, FlyweightFactory 객체를 통해 공유객체 생성 → Unshared 객체를 부르고 Unshared 객체가 share 객체 불러서 합침
-
적용 및 구현
- 객체의 상태: Intrinsic state, Extrinsic state → Intrinsic state: 공유될 수 있는 상태 정보. ConcreteFlyweight 객체에 보관됨 → Extrinsic state: 공유될 수 없는 상태나 상황(context) 정보. Client에서 보관되거나 계산됨 → Extrinsic state가 차지하는 메모리 공간이 적거나, 저장하지 않고도 간단한 계산을 통해서 얻어질 수 있는 경우라면 객체 공유로 얻을 수 있는 효과가 큼 → Intrinsic state가 클 수록 효과적임
- 적용 범위 → 공통되는 부분이 많을 때 & 객체간 구별 필요 없을 때 → 많은 객체의 사용으로 저장 공간에 대한 부담이 클 때 → Extrinsic state 제외하면, 대부분의 객체들이 상대적으로 소수의 공유 객체로 대체될 수 있을 때 → 응용 프로그램이 객체들을 서로 구분할 필요가 없을 때
2021 Spring 최은만 교수님 객체지향 패턴 및 설계 수업 필기노트