[Design Pattern] 프록시 패턴

Proxy Pattern

다른 객체에 대한 접근을 제어하기 위한 대리자 혹은 자리채움자 역할을 하는 객체를 두고 로직의 흐름을 제어하는 패턴이다.

활용성

원격 프록시

원격 프록시는 서로 다른 주소 공간에 존재하는 객체를 가리키는 대표 객체로 로컬 환경에 위치한다. 즉, 고전적인 CS(Client-Server) 아키텍처를 생각하면 된다.

가상 프록시

요청과 실제 사용시점을 분리해 실제 사용 시점에 도달할 때만 필요한 객체를 생성한다. 고비용 객체 생성의 시점을 지연시킴으로써 빠른 로딩이 가능하다.

보호용 프록시

원래 객체에 대한 실제 접근을 제어한다. 즉 요청한 대상에 대한 권한을 검증한다.

스마트 참조자

shared_ptr이 이에 해당한다.

프록시 패턴 사용에 따른 결과

  1. 원격지 프록시는 객체가 다른 주소 공간에 존재한다는 사실을 숨길수 있다.
  2. 가상 프록시는 요구에 따라 객체를 생성하는 등 처리의 최적화가 가능하다.
  3. 보호용 프록시 및 스마트참조자는 객체가 접근할 때마다 추가 관리를 책임진다.

또한 프록시 패턴을 사용하게 되면 또 다른 최적화 방법을 사용할 수 있는데 COW에서 실제 객체가 필요한 시점까지 복사를 지연시키는 방법이다. 이를 변형된 가상 프록시라 하며 자바5의 CopyOnWriteArrayList<E>가 이와 같다.

프록시

구조

프록시의 구조는 다음과 같다.

Proxy Pattern Diagram

프록시의 런타임 구조는 다음과 같다.

Proxy Pattern Runtime Diagram

C++ 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void ProxyPattern::protectedProxy()
{
File* adminFile = new File(AccessAuthority::ADMIN, "This is admin file.");
File* guestFile = new File(AccessAuthority::GUEST, "This is guest file.");

ProtectedFile* guestFileProxy = new ProtectedFile(guestFile);
ProtectedFile* adminFileProxy = new ProtectedFile(adminFile);

std::cout<<guestFileProxy->getData(AccessAuthority::ADMIN)<<std::endl;
std::cout<<guestFileProxy->getData(AccessAuthority::GUEST)<<std::endl;
std::cout<<adminFileProxy->getData(AccessAuthority::ADMIN)<<std::endl;
std::cout<<adminFileProxy->getData(AccessAuthority::GUEST)<<std::endl;
}

void ProxyPattern::virtualProxy()
{
VirtualFile* virtualFile = new VirtualFile("foo path");
std::cout<<virtualFile->getData()<<std::endl;
std::cout<<virtualFile->getData()<<std::endl;
std::cout<<virtualFile->getData()<<std::endl;
std::cout<<virtualFile->getData()<<std::endl;
std::cout<<virtualFile->getData()<<std::endl;
std::cout<<virtualFile->getData()<<std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum AccessAuthority
{
ADMIN,
GUEST,
};

class IFile
{
protected:
std::string data;
AccessAuthority authority;
public:
IFile() {}
IFile(std::string data) : data(std::move(data)) {}
IFile(AccessAuthority authority, std::string data) : data(std::move(data)), authority(authority) {}

virtual ~IFile() {}
virtual std::string getData() = 0;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class File : public IFile
{
public:
File(AccessAuthority authority, std::string data) : IFile(authority, data) {}
File(std::string data) : IFile(data) {}

std::string getData() override
{
return data;
}

AccessAuthority getAuthority()
{
return authority;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class VirtualFile : public IFile
{
std::string path;
File* file;
public:
VirtualFile(std::string path) : path(std::move(path)) {}

std::string getData() override
{
if(file == nullptr) {
file = new File("This is " + path + " file.");
std::cout<<path<<" "<<"file loaded."<<std::endl;
}
return file->getData();
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
struct IllegalAccess : public std::exception
{
const char* msg;
public:
IllegalAccess(const char* msg) : msg(msg) {}

const char* getMessage() const throw()
{
return msg;
}
};

class ProtectedFile : public IFile
{
File* file;

std::string getData() override
{
return file->getData();
}
public:
ProtectedFile(File* file)
{
this->file = file;
}

std::string getData(AccessAuthority authority)
{
switch(authority)
{
case AccessAuthority::ADMIN :
return getData();
case AccessAuthority::GUEST :
if(file->getAuthority() == AccessAuthority::GUEST)
return getData();
else
throw IllegalAccess("Access Denied");
}
if(authority == AccessAuthority::ADMIN)
return getData();

}
};

java 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Client {
public static void main(String[] args) throws Exception {
virtualProxy();
protectedProxy();
}

public static void virtualProxy() {
VirtualProxy virtualProxy = new VirtualProxy("foo file path");
System.out.println(virtualProxy.getData());
System.out.println(virtualProxy.getData());
System.out.println(virtualProxy.getData());
System.out.println(virtualProxy.getData());
System.out.println(virtualProxy.getData());
System.out.println(virtualProxy.getData());
}

public static void protectedProxy() throws Exception {
File guestFile = new FileExt("This is Guest File.", AccessAuthority.GUEST);
File adminFile = new FileExt("This is Admin File.", AccessAuthority.ADMIN);

ProtectedFileProxy guestProxy = new ProtectedFileProxy(guestFile);
ProtectedFileProxy adminProxy = new ProtectedFileProxy(adminFile);

System.out.println(guestProxy.getData(AccessAuthority.ADMIN));
System.out.println(guestProxy.getData(AccessAuthority.GUEST));
System.out.println(adminProxy.getData(AccessAuthority.ADMIN));
System.out.println(adminProxy.getData(AccessAuthority.GUEST));
}
}
1
2
3
4
public enum AccessAuthority {
ADMIN,
GUEST;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class File {
protected String data;
protected AccessAuthority authority;

public File() {}

public File(String data) {
this.data = data;
}

public File(String data, AccessAuthority authority) {
this.data = data;
this.authority = authority;
}

protected abstract String getData();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class FileExt extends File{
public FileExt(String data) {
super(data);
}

public FileExt(String data, AccessAuthority authority) {
super(data, authority);
}

@Override
public String getData() {
return data;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ProtectedFileProxy extends File{
private File file;

public ProtectedFileProxy(File file) {
this.file = file;
}

public String getData(AccessAuthority authority) throws Exception {
switch (authority) {
case ADMIN:
return getData();
case GUEST:
if(file.authority.equals(authority))
return getData();
else
throw new IllegalAccessException("Access Denied");
}
throw new Exception("Not Reachable");
}

@Override
protected String getData() {
return file.getData();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class VirtualProxy extends File{
private String path;
private File file;

public VirtualProxy(String path) {
this.path = path;
}

@Override
protected String getData() {
if(Objects.isNull(file)) {
file = new FileExt("This is " + path + " file.");
System.out.println("File has been loaded");
}

return file.getData();
}
}
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/20/Design%20Pattern/ProxyPattern/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.