객체지향설계와패턴 lecture #11: 구조패턴(2) · pattern #8 –컴포지트패턴...
TRANSCRIPT
학습 목표
l 컴포지트 패턴
l 어뎁터 패턴
l 프록시 패턴
l 실습 문제
2
l 컴포지트 패턴
l 어뎁터 패턴
l 프록시 패턴
l 실습 문제
Pattern #8 – 컴포지트 패턴
l 패턴 요약l 기본 클래스와 이를 포함하는 컨테이너 클래스를 재귀적인 형태로 표현l 객체들의 집합을 다룰 때 유용l e.g. 폴더 안의 파일
객체의 트리를 나타내는 데 사용
3
객체의 트리를 나타내는 데 사용
컴포지트 패턴의 기본 모델
Component
leaf nodenon-leaf node객체
클래스 1..n
4NonLeafNode
Component
“모든 객체는component 객체”
“non-leaf 노드는하나 이상의components”
클래스 1..n
컴포지트 패턴 : 동 기
회사에서당신이맡은업무는 Drawing Tool 을 개발하는것이다.이 Drawing Tool은 직선, 사각형, 원 등의 그림을 그릴 수 있으며, 각개체를 이동 및 속성 수정이 가능하다. 또한 몇 개의 개체를 묶어하나의 Group 개체로만들수도있다.이러한 요구사항을 만족하기 위해서 어떻게 그림 개체 클래스들을설계해야되는가?
회사에서당신이맡은업무는 Drawing Tool 을 개발하는것이다.이 Drawing Tool은 직선, 사각형, 원 등의 그림을 그릴 수 있으며, 각개체를 이동 및 속성 수정이 가능하다. 또한 몇 개의 개체를 묶어하나의 Group 개체로만들수도있다.이러한 요구사항을 만족하기 위해서 어떻게 그림 개체 클래스들을설계해야되는가?
5
컴포지트 패턴 : 동 기
Circle
Rectangle
GroupGroup
Rectangle
Group
LineLineLineLine
Circle
Circle
Line
전체 그림개체들을하나의 group으로 간주
6
Line
Rectangle Line Circle Group
[필요한 클래스들]
Circle Line
Group
LineLineLineLine
Part-Whole 구조
컴포지트 패턴 : 동 기
[추상 클래스 만듦]
Group과 Figure와의관계는?
Rectangle Line Circle Group
Figure
[추상 클래스 만듦]
Group과 Figure와의관계는?
7
Group Figure*
Circle Line
Group
LineLineLineLine
컴포지트 패턴 : 동 기
[연산을 추가한 클래스 구조]
8
Group에 개체 추가및 삭제 연산
leaf or terminal nodes
컴포지트 패턴 : 동 기
l 원시 코드class Figure {public:
virtual void draw() =0;virtual void move() =0;virtual void add(Figure *) {}virtual void remove(Figure *) {}Figure *getChild(int) {}
};
class Rectangle : public Figure {public:
virtual void draw() { … }virtual void move() { … }
};class Line : public Figure {public:
virtual void draw() { … }virtual void move() { … }
};class Circle : public Figure {public:
virtual void draw() { … }virtual void move() { … }
};
class Group : public Figure {public:
virtual void draw() {Figure *f;for( int i=0; (f=getChild(i)) !=NULL ; ++i )
f->draw();} virtual void move() {
…}
// Children Handling Routinevirtual void add(Figure *) { … }virtual void remove(Figure *) { … }Figure *getChild(int) { … }…
};
가지고 있는 모든Figure 객체에
draw 함수 호출
9
class Figure {public:
virtual void draw() =0;virtual void move() =0;virtual void add(Figure *) {}virtual void remove(Figure *) {}Figure *getChild(int) {}
};
class Rectangle : public Figure {public:
virtual void draw() { … }virtual void move() { … }
};class Line : public Figure {public:
virtual void draw() { … }virtual void move() { … }
};class Circle : public Figure {public:
virtual void draw() { … }virtual void move() { … }
};
class Group : public Figure {public:
virtual void draw() {Figure *f;for( int i=0; (f=getChild(i)) !=NULL ; ++i )
f->draw();} virtual void move() {
…}
// Children Handling Routinevirtual void add(Figure *) { … }virtual void remove(Figure *) { … }Figure *getChild(int) { … }…
};
내부 Figure 객체 관리(추가, 삭제, 검색)는 이를 위한자료구조가 필요
컴포지트 패턴
l 의도l Part-whole Hierarchy를 표현하기 위하여 객체들을 트리 구조로 구성l Client 가 개개의 객체와 그룹 객체를 동일하게 취급할 수 있게 만든다.
l 적용 범위l 객체들을 part-whole hierarchy 구조로 표현하고자 할 때l 개개의 객체와 그룹 객체를 동일하게 취급하고자 할 때
10
컴포지트 패턴
구조
11
aComposite
aLeaf
aLeaf
aLeafaLeaf
aLeaf
aCompositeaLeaf[실행시 객체들의 일반적인 구성]
컴포지트 패턴
l 참여 요소l 컴포넌트(Figure)
• 리프들의 공통 인터페이스 정의예) draw(), move()
• Child 객체들을 접근하고 관리하기 위한 인터페이스 정의예) add(Component), remove(Component), getChild(int)
l 리프(Rectangle, Line, Circle)• Child 객체를 가지지 않는 객체• 조합에 이용되는 객체들의 기본적 연산들을 정의
l 컴포지트(Group)• Child 객체들을 가지는 객체• 위임 방식으로 리프들의 공통 인터페이스를 구현
l 클라이언트• 모든 객체들을 컴포넌트 객체로 간주하여 이용
l 참여 요소l 컴포넌트(Figure)
• 리프들의 공통 인터페이스 정의예) draw(), move()
• Child 객체들을 접근하고 관리하기 위한 인터페이스 정의예) add(Component), remove(Component), getChild(int)
l 리프(Rectangle, Line, Circle)• Child 객체를 가지지 않는 객체• 조합에 이용되는 객체들의 기본적 연산들을 정의
l 컴포지트(Group)• Child 객체들을 가지는 객체• 위임 방식으로 리프들의 공통 인터페이스를 구현
l 클라이언트• 모든 객체들을 컴포넌트 객체로 간주하여 이용
12
컴포지트 패턴
l 협동l 클라이언트는 컴포지트 구조에 있는 각 객체들을 이용할 때 컴포넌트 인
터페이스를 사용한다.l 클라이언트가 특정 객체에 어떤 요청을 보내면(함수를 호출하면), 해당
객체가 리프 객체일 경우엔 직접적으로 그 요청이 처리되고, 해당 객체가컴포지트 객체일 경우엔 그 요청은 컴포지트 객체가 가지고 있는 모든 컴포넌트 객체들에게 재 전송된다.
13
Client
aLeafdraw
draw 요청 직접처리component interface
aCompsite
component interfacedraw
aComponent
aComponent
…
draw
drawdraw 요청 재 전송
컴포지트 패턴
l 결과l Primitive 객체와 composite 객체로 구성된 클래스 hierarchy를 정의l Client code가 단순해진다.l 새로운 종류의 컴포넌트 객체를 추가하기 쉽다.
• 기존의 코드를 수정하지 않아도 새 컴포넌트 추가가 가능
14
컴포지트 패턴
l 구현l Explicit parent references
• child 객체에서 parent 객체(composite)로의 이동을 위하여 parent reference를 모든 객체마다 가지게 할 수 있다.
• 이 경우, parent reference 변수와 그 관리는 Component 클래스에서 구현하는 것이 일반적이다.-> Leaf, Composite 클래스에서는 parent의 구현에 대해 신경쓰지않아도 됨
l Sharing Components• child 객체들이 독점적으로 한 composite 객체에만 소속되는 것이
아니라 여러 composite 객체들에 동시에 공유될 수 있다.• parent reference 를 유지하려면, 단일 reference가 아닌 복수
references 를 유지 관리하여야 한다.
l 구현l Explicit parent references
• child 객체에서 parent 객체(composite)로의 이동을 위하여 parent reference를 모든 객체마다 가지게 할 수 있다.
• 이 경우, parent reference 변수와 그 관리는 Component 클래스에서 구현하는 것이 일반적이다.-> Leaf, Composite 클래스에서는 parent의 구현에 대해 신경쓰지않아도 됨
l Sharing Components• child 객체들이 독점적으로 한 composite 객체에만 소속되는 것이
아니라 여러 composite 객체들에 동시에 공유될 수 있다.• parent reference 를 유지하려면, 단일 reference가 아닌 복수
references 를 유지 관리하여야 한다.
15
컴포지트 패턴
l Declaring the child management operations in the Component class
l child 객체들을 관리하는 함수들을 1) Component class에 선언하는 것과 2) Composite class에만 선언하는 것, 둘 간의 장단점은?
1) component에 선언 2) composite에만 선언
16
컴포지트 패턴
l How to manages the child objects?l What is the data structure? tree? linked-list? array? or hash?l Is child ordering important?
-> C++의 경우 STL* 의 List 를 주로 사용. (검색은 Iterator를 이용)
l Who should delete components?l It’s usually best to make a composite responsible for deleting its
children.
l How to manages the child objects?l What is the data structure? tree? linked-list? array? or hash?l Is child ordering important?
-> C++의 경우 STL* 의 List 를 주로 사용. (검색은 Iterator를 이용)
l Who should delete components?l It’s usually best to make a composite responsible for deleting its
children.
17 * Standard Template Library
컴포지트 패턴: 사례 #1
l 컴퓨터 견적l 컴퓨터와 같은 기계장치(Equipment)들은 내부에 작은 장치들로 구성되
어 있는 Part-whole 구조로 구성된다.l 컴퓨터의 전체 가격을 계산하려면, 모니터와 본체가격을 계산해야 하고,
본체가격은 다시 본체를 이루는 case, power, mother board, hard disk, ram, cpu, … 등의 가격의 총합으로 계산할 수 있다.
18
컴퓨터장비
모니터
CPU MotherBoard
본체
HDD
Equipment
Monitor HDD CPU MotherBoard CompositeEquipment
PCBody
컴포지트 패턴: 사례 #1
class Equipment {public:
virtual ~Equipment();
const char* Name() { return _name; }
virtual Watt Power();virtual Currency Price();
virtual void Add(Equipment*);virtual void Remove(Equipment*);virtual Iterator<Equipment*>* CreateIterator();
protected:Equipment(const char*);
private:char* _name;int _price;
};
현 장치의 전체 소비전력과가격을 계산하는 함수들
19
class Equipment {public:
virtual ~Equipment();
const char* Name() { return _name; }
virtual Watt Power();virtual Currency Price();
virtual void Add(Equipment*);virtual void Remove(Equipment*);virtual Iterator<Equipment*>* CreateIterator();
protected:Equipment(const char*);
private:char* _name;int _price;
};
child 객체들을 검색하기위한 Iterator 생성 함수
컴포지트 패턴: 사례 #1
class Monitor : public Equipment {public:
Monitor(const char *name, int price) {_name = strdup(name);_price = price;
}virtual ~Monitor();virtual Watt Power();virtual Currency Price() {
return _price;}
private:int _price;
};
class CPU : public Equipment {…
};class HDD : public Equipment {
…};class MotherBoard : public Equipment {
…};
class CompositeEquipment : public Equipment {public:
virtual ~CompositeEquipment();
virtual Watt Power();virtual Currency Price () {
Iterator<Equipment*>* i = CreateIterator();Currency total = 0;
for (i->First(); !i->IsDone(); i->Next()) {total += i->CurrentItem()->Price();
}delete i;return total;
}
virtual void Add(Equipment*);virtual void Remove(Equipment*);virtual Iterator<Equipment*>* CreateIterator();
protected:CompositeEquipment(const char*);
private:List<Equipment*> _equipment;
};20
class Monitor : public Equipment {public:
Monitor(const char *name, int price) {_name = strdup(name);_price = price;
}virtual ~Monitor();virtual Watt Power();virtual Currency Price() {
return _price;}
private:int _price;
};
class CPU : public Equipment {…
};class HDD : public Equipment {
…};class MotherBoard : public Equipment {
…};
class CompositeEquipment : public Equipment {public:
virtual ~CompositeEquipment();
virtual Watt Power();virtual Currency Price () {
Iterator<Equipment*>* i = CreateIterator();Currency total = 0;
for (i->First(); !i->IsDone(); i->Next()) {total += i->CurrentItem()->Price();
}delete i;return total;
}
virtual void Add(Equipment*);virtual void Remove(Equipment*);virtual Iterator<Equipment*>* CreateIterator();
protected:CompositeEquipment(const char*);
private:List<Equipment*> _equipment;
};
_equipment의Iterator를 이용하여
child 객체 접근
컴포지트 패턴: 사례 #1
// Client CodeMonitor *monitor = new Monitor(“PC Monitor”, 300);CPU *cpu = new CPU(“PC CPU”, 100);HDD *hdd = new HDD(“PC Hard Disk Drive”, 120);MotherBoard *mb = new MotherBoard(“PC MotherBoard”, 50);
PCSet *pcSet = new CompositeEquipment(“PC Set”);PCBody *pcBody = new CompositeEquipment(“PC Body”);
pcBody->Add(cpu);pcBody->Add(hdd);pcBody->Add(MotherBoard);pcSet->Add(monitor);pcSet->Add(pcBody);
cout << pcSet->Price();
class PCSet : public CompositeEquipment {public:
PCSet(const char *name) {_name = strdup(name);
}virtual ~PCSet();
};
class PCBody : public CompositeEquipment {public:
PCBody(const char *name) {_name = strdup(name);
}virtual ~PCBody();
};
21
// Client CodeMonitor *monitor = new Monitor(“PC Monitor”, 300);CPU *cpu = new CPU(“PC CPU”, 100);HDD *hdd = new HDD(“PC Hard Disk Drive”, 120);MotherBoard *mb = new MotherBoard(“PC MotherBoard”, 50);
PCSet *pcSet = new CompositeEquipment(“PC Set”);PCBody *pcBody = new CompositeEquipment(“PC Body”);
pcBody->Add(cpu);pcBody->Add(hdd);pcBody->Add(MotherBoard);pcSet->Add(monitor);pcSet->Add(pcBody);
cout << pcSet->Price();
사례 #2: Bank/Teller Example
Client
reports
Supervisoradd(Employee)
EmployeestateName()
1..n
ClerkstateName()
TellerstateName()
22
Supervisoradd(Employee)
Setup
ClerkstateName()
PresidentstateName()
TellerstateName()
ManagerstateName()
사례 #2: Bank/Teller Example
:Client
stateName()
pete:President
xxxx:Employee
:Setp
doClientTasks()
stateName()
xxxx:Employeexxxx
:Employeexxxx:Employee*
23
stateName()
• Creates the tree of Employee objectswith Pete as President
Pattern #9 – 어뎁터 패턴
l 패턴 요약l 원하는 인터페이스를 추상 클래스로 정의l 이를 상속하는 어뎁터 클래스를 만들고 이를 요구하는 기능을 가진 클래
스(adaptee)와 메시지를 교환하게 함
어플리케이션의 기능을 외부에서 필요한 형태로
수정하여 사용하도록 함
24
어플리케이션의 기능을 외부에서 필요한 형태로
수정하여 사용하도록 함
어뎁터 패턴: 동 기
당신은 금융권 전산화 시스템 개발팀의 일원으로, 통신보안 관련 쪽개발을 맡았다. 개발 기간이 빠듯하긴 하지만 다행히 다른프로젝트를 수행할 때 개발해 놓은 통신보안 코드들이 있어서일정을 맞출 수 있을 것 같다. 그런데 한가지 문제점은, 예전프로젝트 때 개발된 통신보안 코드들은 A사의 통신 보안 프로토콜Library를 사용하는데 비하여, 새 프로젝트에서는 B사의 통신 보안프로토콜 Library를 사용하도록 결정되었다. 재사용하려는 통신보안 코드들을 전면적으로 수정하지 않고 A사의 Library를 B사의Library로 대체할수 있는방법은없을까?
당신은 금융권 전산화 시스템 개발팀의 일원으로, 통신보안 관련 쪽개발을 맡았다. 개발 기간이 빠듯하긴 하지만 다행히 다른프로젝트를 수행할 때 개발해 놓은 통신보안 코드들이 있어서일정을 맞출 수 있을 것 같다. 그런데 한가지 문제점은, 예전프로젝트 때 개발된 통신보안 코드들은 A사의 통신 보안 프로토콜Library를 사용하는데 비하여, 새 프로젝트에서는 B사의 통신 보안프로토콜 Library를 사용하도록 결정되었다. 재사용하려는 통신보안 코드들을 전면적으로 수정하지 않고 A사의 Library를 B사의Library로 대체할수 있는방법은없을까?
25
당신은 금융권 전산화 시스템 개발팀의 일원으로, 통신보안 관련 쪽개발을 맡았다. 개발 기간이 빠듯하긴 하지만 다행히 다른프로젝트를 수행할 때 개발해 놓은 통신보안 코드들이 있어서일정을 맞출 수 있을 것 같다. 그런데 한가지 문제점은, 예전프로젝트 때 개발된 통신보안 코드들은 A사의 통신 보안 프로토콜Library를 사용하는데 비하여, 새 프로젝트에서는 B사의 통신 보안프로토콜 Library를 사용하도록 결정되었다. 재사용하려는 통신보안 코드들을 전면적으로 수정하지 않고 A사의 Library를 B사의Library로 대체할수 있는방법은없을까?
당신은 금융권 전산화 시스템 개발팀의 일원으로, 통신보안 관련 쪽개발을 맡았다. 개발 기간이 빠듯하긴 하지만 다행히 다른프로젝트를 수행할 때 개발해 놓은 통신보안 코드들이 있어서일정을 맞출 수 있을 것 같다. 그런데 한가지 문제점은, 예전프로젝트 때 개발된 통신보안 코드들은 A사의 통신 보안 프로토콜Library를 사용하는데 비하여, 새 프로젝트에서는 B사의 통신 보안프로토콜 Library를 사용하도록 결정되었다. 재사용하려는 통신보안 코드들을 전면적으로 수정하지 않고 A사의 Library를 B사의Library로 대체할수 있는방법은없을까?
ClientCode
AProtocolconnect()send()receive()disconnect()
[기존시스템]
ClientCode
BProtocolsetTarget()open()write()read()close()
[개발시스템]
A사 Library Class B사 Library Class
어뎁터 패턴: 동 기
l 일반적 접근 방법l 기존의 Client Code 내에서 A Library(AProtocol) 부분을 모두 B
Library(BProtocol)로 대체하고, AProtocol 객체의 함수 호출부분을 BProtocol 객체의 함수 호출로 바꾼다.
// 기존 Client Code#include “AProtocol.h”
AProtocol proto;proto.connect(“xyz.com”,50);proto.send(msg);…proto.receive(msg);…proto.disconnect();
// 수정된 Client Code#include “BProtocol.h”
BProtocol proto;proto.setTarget(“xyz.com”,50);proto.open();proto.write(msg);…proto.read(msg);…proto.close();
26
// 기존 Client Code#include “AProtocol.h”
AProtocol proto;proto.connect(“xyz.com”,50);proto.send(msg);…proto.receive(msg);…proto.disconnect();
// 수정된 Client Code#include “BProtocol.h”
BProtocol proto;proto.setTarget(“xyz.com”,50);proto.open();proto.write(msg);…proto.read(msg);…proto.close();
문제점: 기존 Client 코드 전체에 산재해 있는 A Library 관련 코드를 모두 수정하여야 한다.
어뎁터 패턴: 동 기
l 해결방안l 기존 Client Code 를 그대도 재 사용하기 위해서는 BProtocol 인터페이
스를 AProtocol의 인터페이스로 변환하는 것이 필요하다. l BProtocol 코드를 직접 변경할 수는 없으므로, Client Code와
BProtocol 사이에 중재자 역할을 하는 새로운 클래스를 작성한다(새로운클래스 이름은 A Library의 클래스 명인 AProtocol로 설정)
// 기존 Client Code#include “AProtocol.h”
AProtocol proto;proto.connect(“xyz.com”,50);proto.send(msg);…proto.receive(msg);…proto.disconnect();
BProtocolsetTarget()open()write()read()close()
AProtocolconnect()send()receive()disconnect()
27
// 기존 Client Code#include “AProtocol.h”
AProtocol proto;proto.connect(“xyz.com”,50);proto.send(msg);…proto.receive(msg);…proto.disconnect();
BProtocolsetTarget()open()write()read()close()
AProtocolconnect()send()receive()disconnect()
A Library의 클래스가 아니고,새로 작성된 클래스
어뎁터 패턴: 동 기
class AProtocol {public:
void connect(char *url, int port) {_bp->setTarget(url, port);_bp->open();
}void send(char *msg) {
_bp->write(msg);}void receive(char *msg) {
_bp->write(msg);}void disconnect() {
_bp->close();}void setBProtocol(BProtocol *bp) {
_bp = bp;}
private:BProtocol *_bp;
};
ClientCode
BProtocol객체
28
class AProtocol {public:
void connect(char *url, int port) {_bp->setTarget(url, port);_bp->open();
}void send(char *msg) {
_bp->write(msg);}void receive(char *msg) {
_bp->write(msg);}void disconnect() {
_bp->close();}void setBProtocol(BProtocol *bp) {
_bp = bp;}
private:BProtocol *_bp;
};
ClientCode
Client Code가AProtocol Interface를 가지고BProtocol 객체를이용할 수 있도록만들어 줌
어뎁터 패턴: 구 조
Client
AbstractClassclientNameForRequiredMethod()
AdapterclientNameForRequiredMethod()
adaptee
RequiredClassrequiredMethod()
29
AdapterclientNameForRequiredMethod()
{ adaptee. requiredMethod();}
어뎁터 패턴: Sequence Diagram
:Client
clientNameForRequiredMethod()
:AbstractClass :Adapter
RequiredMethod()
adaptee:RequiredClass
30
어뎁터 패턴
l 의 도l 특정 클래스의 인터페이스를 클라이언트가 원하는 인터페이스로 변환한다.
l 별 명l Wrapper
l 적용 분야l 기존에 존재하는 클래스를 재사용하고 싶지만, 원하는 인터페이스와 맞지 않을 때l 미리 정해져 있지 않은 클래스들과 상호 작용할 수 있는 재사용 가능 클래스를 만
들려는 경우
l 의 도l 특정 클래스의 인터페이스를 클라이언트가 원하는 인터페이스로 변환한다.
l 별 명l Wrapper
l 적용 분야l 기존에 존재하는 클래스를 재사용하고 싶지만, 원하는 인터페이스와 맞지 않을 때l 미리 정해져 있지 않은 클래스들과 상호 작용할 수 있는 재사용 가능 클래스를 만
들려는 경우
31
어뎁터 패턴
l 구 조l Class adapter: 상속을 이용
l Object adapter: 위임을 이용
l 구 조l Class adapter: 상속을 이용
l Object adapter: 위임을 이용
32
어뎁터 패턴
l 구성 요소l Target (AProtocol)
• 클라이언트가 사용하는 도메인 종속적인 인터페이스를 정의한다.
l Client• Target 인터페이스를 통하여 Adaptee 기능을 활용한다.
l Adaptee (BProtocol)• 인터페이스 변화가 필요한 클래스
l Adapter (AProtocol)• Target interface를 통하여 들어오는 클라이언트의 요청을 Adaptee에게 전달해주는 역
할 수행
l 협 동l Client는 Adapter의 함수를 호출하면, Adapter는 Adapter에게 해당 요청을 수행
하는 함수를 호출한다.
l 구성 요소l Target (AProtocol)
• 클라이언트가 사용하는 도메인 종속적인 인터페이스를 정의한다.
l Client• Target 인터페이스를 통하여 Adaptee 기능을 활용한다.
l Adaptee (BProtocol)• 인터페이스 변화가 필요한 클래스
l Adapter (AProtocol)• Target interface를 통하여 들어오는 클라이언트의 요청을 Adaptee에게 전달해주는 역
할 수행
l 협 동l Client는 Adapter의 함수를 호출하면, Adapter는 Adapter에게 해당 요청을 수행
하는 함수를 호출한다.
33
Client Adapter Adapteemsg1 msg1’
어뎁터 패턴
l 결과l How much adapting does Adapter do?
• Adapter와 Adaptee 간의 inteface 차이 정도에 따라 작업의 수준이달라짐.
l Trade-offs of Class and Object adapters
l 구현l Implementing class adapters in C++
• 일반적으로 Adapter class 가 Target class는 public으로, Adaptee class는 private으로 상속 받는다.
l 결과l How much adapting does Adapter do?
• Adapter와 Adaptee 간의 inteface 차이 정도에 따라 작업의 수준이달라짐.
l Trade-offs of Class and Object adapters
l 구현l Implementing class adapters in C++
• 일반적으로 Adapter class 가 Target class는 public으로, Adaptee class는 private으로 상속 받는다.
34
어뎁터 패턴: 사례 #1
l 그래픽 에디터 프로그램l 그래픽 객체로 Line 과 Text 가 존재한다.l TextShape 구현이 복잡한 관계로 기존에 존재하는 TextView 클래스 사
용.
Target Adaptee
35 Adapter
어뎁터 패턴: 사례 #1
l Shape, TextView code
class Shape {public:
Shape();virtual void BoundingBox(Point& bottomLeft, Point& topRight) const;virtual Manipulator* CreateManipulator() const;
};
class TextView {public:
TextView();void GetOrigin(Coord& x, Coord& y) const;void GetExtent(Coord& width, Coord& height) const;virtual bool IsEmpty() const;
};
36
class Shape {public:
Shape();virtual void BoundingBox(Point& bottomLeft, Point& topRight) const;virtual Manipulator* CreateManipulator() const;
};
class TextView {public:
TextView();void GetOrigin(Coord& x, Coord& y) const;void GetExtent(Coord& width, Coord& height) const;virtual bool IsEmpty() const;
};
어뎁터 패턴: 사례 #1
l Class Adapter 방식으로 구현
class TextShape : public Shape, private TextView {public:
TextShape();
virtual void BoundingBox( Point& bottomLeft, Point& topRight ) const;virtual bool IsEmpty() const;virtual Manipulator* CreateManipulator() const;
};
void TextShape::BoundingBox ( Point& bottomLeft, Point& topRight ) const {Coord bottom, left, width, height;
GetOrigin(bottom, left);GetExtent(width, height);bottomLeft = Point(bottom, left);topRight = Point(bottom + height, left + width);
}
Target은 public, Adaptee는 private
37
class TextShape : public Shape, private TextView {public:
TextShape();
virtual void BoundingBox( Point& bottomLeft, Point& topRight ) const;virtual bool IsEmpty() const;virtual Manipulator* CreateManipulator() const;
};
void TextShape::BoundingBox ( Point& bottomLeft, Point& topRight ) const {Coord bottom, left, width, height;
GetOrigin(bottom, left);GetExtent(width, height);bottomLeft = Point(bottom, left);topRight = Point(bottom + height, left + width);
}
Adaptee(TextView) 함수 호출
어뎁터 패턴: 사례 #1
l Object Adapter 방식으로 구현
class TextShape : public Shape {public:
TextShape(TextView*) {_text = t;
}virtual void BoundingBox( Point& bottomLeft, Point& topRight ) const;virtual bool IsEmpty() const;virtual Manipulator* CreateManipulator() const;
private:TextView* _text;
};
void TextShape::BoundingBox ( Point& bottomLeft, Point& topRight) const {Coord bottom, left, width, height;
_text->GetOrigin(bottom, left);_text->GetExtent(width, height);
bottomLeft = Point(bottom, left);topRight = Point(bottom + height, left + width);
}
Adaptee(TextView) 객체 지정
38
class TextShape : public Shape {public:
TextShape(TextView*) {_text = t;
}virtual void BoundingBox( Point& bottomLeft, Point& topRight ) const;virtual bool IsEmpty() const;virtual Manipulator* CreateManipulator() const;
private:TextView* _text;
};
void TextShape::BoundingBox ( Point& bottomLeft, Point& topRight) const {Coord bottom, left, width, height;
_text->GetOrigin(bottom, left);_text->GetExtent(width, height);
bottomLeft = Point(bottom, left);topRight = Point(bottom + height, left + width);
}
지정된 Adaptee 객체 함수호출
Pattern #10 – 프록시 패턴
l 패턴 요약l 특정 객체에 접근을 조절하기 위하여 대리자(프록시)를 세움l 필요할 때만 비싼 대가의 기능을 접근하도록 프록시를 사이에 둠
시간이 많이 소요되는 불필요한 복잡한 객체를 생성하는
시간을 간단한 객체로 줄임
39
시간이 많이 소요되는 불필요한 복잡한 객체를 생성하는
시간을 간단한 객체로 줄임
프록시 패턴: 동 기
당신이 이번에 참여한 프로젝트는 그래픽을 지원하는 문서편집기를업그레이드하는 작업이다. 사용자들이 기존의 문서편집기를 사용할 때 가장큰 불만사항은 파일을 여는데 속도가 너무 느리다는 점이다. 이유를분석해보니 파일을 열 때 문서에 포함된 그림들을 모두 메모리에 올리고, 또압축된그림데이터를 Bitmap으로변환하는작업을수행하기때문이다.문서의 모든 그림들을 사용자가 보는 경우는 드물다는 점을 이용해서,사용자가 실제로 보는 그림(화면에 출력을 요구 받은 그림 객체) 들만메모리에 올린다면, 파일 여는 시간을 훨씬 단축시킬 수 있을 것이다. 기존의설계를어떻게변경해야될까?
당신이 이번에 참여한 프로젝트는 그래픽을 지원하는 문서편집기를업그레이드하는 작업이다. 사용자들이 기존의 문서편집기를 사용할 때 가장큰 불만사항은 파일을 여는데 속도가 너무 느리다는 점이다. 이유를분석해보니 파일을 열 때 문서에 포함된 그림들을 모두 메모리에 올리고, 또압축된그림데이터를 Bitmap으로변환하는작업을수행하기때문이다.문서의 모든 그림들을 사용자가 보는 경우는 드물다는 점을 이용해서,사용자가 실제로 보는 그림(화면에 출력을 요구 받은 그림 객체) 들만메모리에 올린다면, 파일 여는 시간을 훨씬 단축시킬 수 있을 것이다. 기존의설계를어떻게변경해야될까?
40
당신이 이번에 참여한 프로젝트는 그래픽을 지원하는 문서편집기를업그레이드하는 작업이다. 사용자들이 기존의 문서편집기를 사용할 때 가장큰 불만사항은 파일을 여는데 속도가 너무 느리다는 점이다. 이유를분석해보니 파일을 열 때 문서에 포함된 그림들을 모두 메모리에 올리고, 또압축된그림데이터를 Bitmap으로변환하는작업을수행하기때문이다.문서의 모든 그림들을 사용자가 보는 경우는 드물다는 점을 이용해서,사용자가 실제로 보는 그림(화면에 출력을 요구 받은 그림 객체) 들만메모리에 올린다면, 파일 여는 시간을 훨씬 단축시킬 수 있을 것이다. 기존의설계를어떻게변경해야될까?
당신이 이번에 참여한 프로젝트는 그래픽을 지원하는 문서편집기를업그레이드하는 작업이다. 사용자들이 기존의 문서편집기를 사용할 때 가장큰 불만사항은 파일을 여는데 속도가 너무 느리다는 점이다. 이유를분석해보니 파일을 열 때 문서에 포함된 그림들을 모두 메모리에 올리고, 또압축된그림데이터를 Bitmap으로변환하는작업을수행하기때문이다.문서의 모든 그림들을 사용자가 보는 경우는 드물다는 점을 이용해서,사용자가 실제로 보는 그림(화면에 출력을 요구 받은 그림 객체) 들만메모리에 올린다면, 파일 여는 시간을 훨씬 단축시킬 수 있을 것이다. 기존의설계를어떻게변경해야될까?
사용자가 한번도 안보고 문서를 닫을 수도 있는데미리 그림 객체를 만들 필요가 있을까?
this is the first you
r testing about the
airplains this is
the first your
testing about the airplains
this is the first te
sting a this
is is the first testing about
실제 화면에 출력할 필요가 있는 그림 객체만 생성
프록시 패턴: 동 기
l 기존 설계
// Client Code
Document *doc = new Document;…doc->Insert(new Image(“chart1.jpg”));…doc->drawPage(0); // draws the first page
image 객체 생성
41
// Client Code
Document *doc = new Document;…doc->Insert(new Image(“chart1.jpg”));…doc->drawPage(0); // draws the first page
어떻게 하면 Image 객체 생성을 해당객체가 필요할 때까지 미룰 수 있을까? 어떻게 하면 Image 객체 생성을 해당
객체가 필요할 때까지 미룰 수 있을까?
프록시 패턴: 동 기
l 해결방안l 대리자(Proxy, Agent) 를 내세우기l proxy는 기존의 코드에서 image 객체를 대신함l proxy는 요청을 받으면, 자신이 대신하고 있는 image객체에 해당 요청을 보냄l proxy 객체가 처음 생성될 때는 자신이 대신할 image객체를 미리 만들지 않고,
외부에서 처음 요청이 들어왔을 때(즉, image 객체가 필요할 때) image 객체를생성.
this is the first te
sting a this
is is the first testing about
42
this is the first you
r testing about the
airplains this is
the first your
testing about the airplains
this is the first te
sting a this
is is the first testing about
aImageProxy
aImageProxy
aImageProxy
aImageProxy
aImageProxy
aImage
this is the first you
r testing about the
airplains this is
the first your
testing about the airplains
this is the first te
sting a this
is is the first testing about
aImageProxy
aImageProxy
aImageProxy
aImageProxy
aImageProxy
aImage
aImage
생성
프록시 패턴: 동 기
l Class diagram
43
// Client Code
Document *doc = new Document;…doc->Insert(new ImageProxy(“chart1.jpg”));…doc->drawPage(0); // draws the first page
Image 객체 대신ImageProxy 객체 사용
프록시 패턴
l 의도l 특정 객체에 대한 접근(access)을 조절하기 위하여 대리자를 둔다.
l 별명l Surrogate
l 적용 범위l remote proxy: 다른 주소에 존재, 다른 공간에 존재하는 객체에 대한 로
컬 표현l virtual proxy: 복잡한 객체를 필요할 때 생성l protection proxy: 원래 객체에 대한 억세스 권한을 제한l smart reference: 객체접근 시 추가적인 액션 수행. 참조회수를 관리하거
나 객체를 수정할 수 없도록 locking
l 의도l 특정 객체에 대한 접근(access)을 조절하기 위하여 대리자를 둔다.
l 별명l Surrogate
l 적용 범위l remote proxy: 다른 주소에 존재, 다른 공간에 존재하는 객체에 대한 로
컬 표현l virtual proxy: 복잡한 객체를 필요할 때 생성l protection proxy: 원래 객체에 대한 억세스 권한을 제한l smart reference: 객체접근 시 추가적인 액션 수행. 참조회수를 관리하거
나 객체를 수정할 수 없도록 locking
44
프록시 패턴
l 구 조
l [객체다이어그램]
l 구 조
l [객체다이어그램]
45
aClientsubject aProxy
subject aRealSubject
프록시 패턴
l 구성요소l Proxy(ImageProxy)
• RealSubject 객체에 대한 reference 를 유지한다.• Subject interface를 구현하여 RealSubject 객체를 대신할 수 있다.• RealSubject 객체에 대한 접근을 조절하며, 해당 객체의 생성과 소멸
을 책임질 수 도 있다.l Subject(Graphic)
• RealSubject 와 Proxy의 공통 interface를 정의한다.l RealSubject(Image)
• Proxy가 대신할 실제 객체를 정의한다.
l 협동l Proxy는 client의 요청을 RealSubject 객체에게 보낸다.
l 구성요소l Proxy(ImageProxy)
• RealSubject 객체에 대한 reference 를 유지한다.• Subject interface를 구현하여 RealSubject 객체를 대신할 수 있다.• RealSubject 객체에 대한 접근을 조절하며, 해당 객체의 생성과 소멸
을 책임질 수 도 있다.l Subject(Graphic)
• RealSubject 와 Proxy의 공통 interface를 정의한다.l RealSubject(Image)
• Proxy가 대신할 실제 객체를 정의한다.
l 협동l Proxy는 client의 요청을 RealSubject 객체에게 보낸다.
46
프록시 패턴
l 결과l remote proxy는 객체가 다른 주소공간에 존재하는 사실을 숨길 수 있게
한다.l virtual proxy는 객체를 필요할 때 생성하여 최적화를 수행한다.l protection proxy와 smart reference는 객체가 접근할 때 추가적인
housekeeping 작업을 수행한다.l 불필요한 다운로드 또는 heavy work을 피한다.
47
프록시 패턴
l 구현l 접근 연산자 (‘->’ in C++) 를 재정의하기
• RealSubject 객체 접근에 대한 조정기능을 각 함수마다 넣는 것이 아니라, 접근 연산자를 재정의하여 여기에 넣는다.
l Proxy 객체가 항상 RealSubject의 class를 알고 있을 필요는 없다.• Proxy 객체가 RealSubject 객체를 직접 접근하지 않고, Subject
interface를 사용하여 객체를 다룸으로써 범용적인 Proxy로 만들 수있다.
l 구현l 접근 연산자 (‘->’ in C++) 를 재정의하기
• RealSubject 객체 접근에 대한 조정기능을 각 함수마다 넣는 것이 아니라, 접근 연산자를 재정의하여 여기에 넣는다.
l Proxy 객체가 항상 RealSubject의 class를 알고 있을 필요는 없다.• Proxy 객체가 RealSubject 객체를 직접 접근하지 않고, Subject
interface를 사용하여 객체를 다룸으로써 범용적인 Proxy로 만들 수있다.
48
프록시 패턴: 사례 #1
[접근 연산자 재정의 사례 코드]
class Image;// external functionextern Image* LoadAnImageFile(const char*);
class ImagePtr {public:
ImagePtr (const char* theImageFile) {_imageFile = theImageFile;_image = 0;
}virtual ~ImagePtr();virtual Image* operator->() { return LoadImage(); }virtual Image& operator*() { return *LoadImage(); }
private:Image* LoadImage() {
if (_image == NULL) _image = LoadAnImageFile(_imageFile);
return _image;}
private:Image* _image;const char* _imageFile;
};
// Client Code
ImagePtr image = ImagePtr(“chart1.jpg”);image->Draw(100,100);// same as (image.operator->()) ->Draw(100,100);
pointer가 아닌 image가마치 Image 객체의pointer처럼 사용
49
class Image;// external functionextern Image* LoadAnImageFile(const char*);
class ImagePtr {public:
ImagePtr (const char* theImageFile) {_imageFile = theImageFile;_image = 0;
}virtual ~ImagePtr();virtual Image* operator->() { return LoadImage(); }virtual Image& operator*() { return *LoadImage(); }
private:Image* LoadImage() {
if (_image == NULL) _image = LoadAnImageFile(_imageFile);
return _image;}
private:Image* _image;const char* _imageFile;
};
// Client Code
ImagePtr image = ImagePtr(“chart1.jpg”);image->Draw(100,100);// same as (image.operator->()) ->Draw(100,100);
reference, dereference 연산자 재 정의
프록시 패턴
l 사례 코드l 문제 해결방안에 대한 소스코드
class Graphic {public:
virtual ~Graphic();virtual void Draw(const Point& at) = 0;virtual const Point& GetExtent() = 0;virtual void Load(istream& from) = 0;virtual void Save(ostream& to) = 0;
protected:Graphic();
};
class Image : public Graphic {public:
Image(const char* file); // loads image from a filevirtual ~Image();virtual void Draw(const Point& at);virtual const Point& GetExtent();virtual void Load(istream& from);virtual void Save(ostream& to);
private:// ...
};50
class Graphic {public:
virtual ~Graphic();virtual void Draw(const Point& at) = 0;virtual const Point& GetExtent() = 0;virtual void Load(istream& from) = 0;virtual void Save(ostream& to) = 0;
protected:Graphic();
};
class Image : public Graphic {public:
Image(const char* file); // loads image from a filevirtual ~Image();virtual void Draw(const Point& at);virtual const Point& GetExtent();virtual void Load(istream& from);virtual void Save(ostream& to);
private:// ...
};
프록시 패턴class ImageProxy : public Graphic {public:
ImageProxy(const char* fileName) {_fileName = strdup(fileName);_extent = Point::Zero; // don't know extent yet_image = NULL;
}virtual ~ImageProxy();virtual void Draw(const Point& at) {
GetImage()->Draw(at);}virtual const Point& GetExtent() {
if (_extent == Point::Zero) _extent = GetImage()->GetExtent();
return _extent;}virtual void Load(istream& from);virtual void Save(ostream& to);
protected:Image* GetImage() {
if (_image == 0) _image = new Image(_fileName);
return _image;}
private:Image* _image;Point _extent;char* _fileName;
};
class Document {public:
Document();~Document(); //void Insert(Graphic*);// ...
};
// Client CodeDocument* doc = new Document;// ...doc->Insert(new ImageProxy(“chart1.jpg"));
51
class ImageProxy : public Graphic {public:
ImageProxy(const char* fileName) {_fileName = strdup(fileName);_extent = Point::Zero; // don't know extent yet_image = NULL;
}virtual ~ImageProxy();virtual void Draw(const Point& at) {
GetImage()->Draw(at);}virtual const Point& GetExtent() {
if (_extent == Point::Zero) _extent = GetImage()->GetExtent();
return _extent;}virtual void Load(istream& from);virtual void Save(ostream& to);
protected:Image* GetImage() {
if (_image == 0) _image = new Image(_fileName);
return _image;}
private:Image* _image;Point _extent;char* _fileName;
};
class Document {public:
Document();~Document(); //void Insert(Graphic*);// ...
};
// Client CodeDocument* doc = new Document;// ...doc->Insert(new ImageProxy(“chart1.jpg"));
프록시 패턴: 사례 #2
l 프록시 프린터
52
Proxy Example TelNumsvalue: Vector
getTelNums(): VectorshowMiddleRecord()
RemoteTelNumsgetTelNums()
TelNumsProxygetTelNums()
TelephoneAppdisplay( TelNums )
display MiddleRecord()
remoteTelNums
static
1
53
RemoteTelNumsgetTelNums()
TelNumsProxygetTelNums()
. . . // One way to check if really needed:if ( value == null ) // never referenced
remoteTelNums.getTelNums(); else // no need to call ‘getTelNums()’
SetupEnsures that TelephoneAppmakes calls with TelNumsProxyinstance
237점157점464점
교
Questions?
237점157점464점
교
Questions?