의도
프록시는 다른 객체에 대한 대체 또는 자리표시자를 제공할 수 있는 구조 디자인 패턴입니다. 프록시는 원래 객체에 대한 접근을 제어하므로, 당신의 요청이 원래 객체에 전달되기 전 또는 후에 무언가를 수행할 수 있도록 합니다.

문제
객체에 대한 접근을 제한하는 이유는 무엇일까요? 이 질문에 답하기 위하여 방대한 양의 시스템 자원을 소비하는 거대한 객체가 있다고 가정합시다. 이 객체는 필요할 때가 있기는 하지만, 항상 필요한 것은 아닙니다.

데이터베이스 쿼리들은 정말 느릴 수 있습니다.
당신은 실제로 필요할 때만 이 객체를 만들어서 지연된 초기화를 구현할 수 있습니다. 그러면 객체의 모든 클라이언트들은 어떤 지연된 초기화 코드를 실행해야 하는데, 불행히도 이것은 아마도 많은 코드 중복을 초래할 것입니다.
이상적인 상황에서는 이 코드를 객체의 클래스에 직접 넣을 수 있겠지만, 그게 항상 가능한 것은 아닙니다. 예를 들어 그 클래스가 폐쇄된 타사 라이브러리의 일부일 수 있습니다.
해결책
프록시 패턴은 원래 서비스 객체와 같은 인터페이스로 새 프록시 클래스를 생성하라고 제안합니다. 그러면 프록시 객체를 원래 객체의 모든 클라이언트들에 전달하도록 앱을 업데이트할 수 있습니다. 클라이언트로부터 요청을 받으면 이 프록시는 실제 서비스 객체를 생성하고 모든 작업을 이 객체에 위임합니다.

프록시는 데이터베이스 객체로 자신을 변장합니다. 프록시는 지연된 초기화 및 결괏값 캐싱을 클라이언트와 실제 데이터베이스 객체가 알지 못하는 상태에서 처리할 수 있습니다.
그러나 이것들은 무슨 소용이 있는지 궁금하실 것입니다. 당신이 클래스의 메인 로직 이전이나 이후에 무언가를 실행해야 하는 경우 프록시는 해당 클래스를 변경하지 않고도 이 무언가를 수행할 수 있도록 합니다. 프록시는 원래 클래스와 같은 인터페이스를 구현하므로 실제 서비스 객체를 기대하는 모든 클라이언트에 전달될 수 있습니다.
실제상황 적용

신용 카드는 현금과 마찬가지로 결제에 사용할 수 있습니다.
신용 카드는 은행 계좌의 프록시이며, 은행 계좌는 현금의 프록시입니다. 둘 다 같은 인터페이스를 구현하며 둘 다 결제에 사용될 수 있습니다. 신용 카드를 사용하는 소비자는 많은 현금을 가지고 다닐 필요가 없어서 기분이 좋습니다. 또한 상점 주인은 거래 수입을 은행에 가는 길에 강도를 당하거나 잃어버릴 위험 없이 계좌에 전자적으로 입금이 되기 때문에 기분이 좋습니다.
#include <iostream>
/**
* The Subject interface declares common operations for both RealSubject and the
* Proxy. As long as the client works with RealSubject using this interface,
* you'll be able to pass it a proxy instead of a real subject.
*/
class Subject {
public:
virtual void Request() const = 0;
};
/**
* The RealSubject contains some core business logic. Usually, RealSubjects are
* capable of doing some useful work which may also be very slow or sensitive -
* e.g. correcting input data. A Proxy can solve these issues without any
* changes to the RealSubject's code.
*/
class RealSubject : public Subject {
public:
void Request() const override {
std::cout << "RealSubject: Handling request.\n";
}
};
/**
* The Proxy has an interface identical to the RealSubject.
*/
class Proxy : public Subject {
/**
* @var RealSubject
*/
private:
RealSubject *real_subject_;
bool CheckAccess() const {
// Some real checks should go here.
std::cout << "Proxy: Checking access prior to firing a real request.\n";
return true;
}
void LogAccess() const {
std::cout << "Proxy: Logging the time of request.\n";
}
/**
* The Proxy maintains a reference to an object of the RealSubject class. It
* can be either lazy-loaded or passed to the Proxy by the client.
*/
public:
Proxy(RealSubject *real_subject) : real_subject_(new RealSubject(*real_subject)) {
}
~Proxy() {
delete real_subject_;
}
/**
* The most common applications of the Proxy pattern are lazy loading,
* caching, controlling the access, logging, etc. A Proxy can perform one of
* these things and then, depending on the result, pass the execution to the
* same method in a linked RealSubject object.
*/
void Request() const override {
if (this->CheckAccess()) {
this->real_subject_->Request();
this->LogAccess();
}
}
};
/**
* The client code is supposed to work with all objects (both subjects and
* proxies) via the Subject interface in order to support both real subjects and
* proxies. In real life, however, clients mostly work with their real subjects
* directly. In this case, to implement the pattern more easily, you can extend
* your proxy from the real subject's class.
*/
void ClientCode(const Subject &subject) {
// ...
subject.Request();
// ...
}
int main() {
std::cout << "Client: Executing the client code with a real subject:\n";
RealSubject *real_subject = new RealSubject;
ClientCode(*real_subject);
std::cout << "\n";
std::cout << "Client: Executing the same client code with a proxy:\n";
Proxy *proxy = new Proxy(real_subject);
ClientCode(*proxy);
delete real_subject;
delete proxy;
return 0;
}
'디자인 패턴' 카테고리의 다른 글
Command 패턴 (0) | 2024.09.04 |
---|---|
Chain of Responsibility 패턴 (0) | 2024.07.25 |
Flyweight 패턴 (0) | 2024.07.25 |
Facade 패턴 (0) | 2024.07.25 |
Decorator 패턴 (0) | 2024.07.11 |