代理パターンの基本実装では、クライアントが直接$numをインスタンス化して代理オブジェクトを生成し、_ru对象をデリゲートできるため、!proxyチェックの単純な条件分岐だけでは、本体オブジェクトが内部で代理プロパティを保持した場合に、代理経由でないアクセスが可能になるという問題があります。このため、本体が自由に自らのメソッドを呼び出せる状態になり、代理の意味が薄れてしまいます。
強制代理では、以下の制約を厳密に適用します:
- 代理経由でのみメソッド呼び出しが可能
- 本体オブジェクトは、自身の代理以外ではメソッドを実行できない
- new で直接生成された代理オブジェクトは拒否される
以下はその一つの実装例です。ここでは、 Proxy への参照のみ有効であることを実行時に検証し、他の呼び出しを拒否する設計を ======= 上 ======== 現します。
// SellingTickets.h
#ifndef SELLING_TICKETS_H
#define SELLING_TICKETS_H
#include <iostream>
#include <string>
namespace control {
class Proxy;
class SellingTickets {
public:
virtual ~SellingTickets() = default;
virtual void sell(Proxy* p) = 0;
virtual void pricing(Proxy* p) = 0;
};
} // namespace control
#endif // SELLING_TICKETS_H
// Tickets.h
#ifndef TICKETS_H
#define TICKETS_H
#include "SellingTickets.h"
namespace control {
class Proxy;
class Tickets : public SellingTickets {
public:
explicit Tickets(std::string type);
~Tickets();
Proxy* acquireProxy();
void sell(Proxy* p) override;
void pricing(Proxy* p) override;
private:
void sellInternal();
void pricingInternal();
bool validateProxy(Proxy* p) const;
private:
std::string _ticketType;
Proxy* _validProxy = nullptr;
};
} // namespace control
#endif // TICKETS_H
// Proxy.h
#ifndef PROXY_H
#define PROXY_H
#include "SellingTickets.h"
namespace control {
class Tickets;
class Proxy : public SellingTickets {
public:
explicit Proxy(Tickets* host);
void sell(Proxy* p) override;
void pricing(Proxy* p) override;
private:
Tickets* _host;
};
} // namespace control
#endif // PROXY_H
// Tickets.cpp
#include "Tickets.h"
#include "Proxy.h"
namespace control {
Tickets::Tickets(std::string type) : _ticketType(std::move(type)) {}
Tickets::~Tickets() {
if (_validProxy) {
delete _validProxy;
}
}
Proxy* Tickets::acquireProxy() {
if (_validProxy) {
return nullptr; // 既に生成済みのため新規作成を禁止
}
_validProxy = new Proxy(this);
return _validProxy;
}
void Tickets::sell(Proxy* p) {
if (validateProxy(p)) {
sellInternal();
} else {
std::cerr << "Access denied: invalid proxy\n";
}
}
void Tickets::pricing(Proxy* p) {
if (validateProxy(p)) {
pricingInternal();
} else {
std::cerr << "Access denied: invalid proxy\n";
}
}
bool Tickets::validateProxy(Proxy* p) const {
return p && (p == _validProxy);
}
void Tickets::sellInternal() {
std::cout << "Selling ticket: " << _ticketType << "\n";
}
void Tickets::pricingInternal() {
std::cout << "Price: ¥100\n";
}
} // namespace control
// Proxy.cpp
#include "Proxy.h"
#include "Tickets.h"
namespace control {
Proxy::Proxy(Tickets* host) : _host(host) {}
void Proxy::sell(Proxy* p) {
_host->sell(p); // 引数を渡して委譲
}
void Proxy::pricing(Proxy* p) {
_host->pricing(p);
}
} // namespace control
// main.cpp
#include "Tickets.h"
#include "Proxy.h"
#include <iostream>
using namespace control;
int main() {
auto* ticket = new Tickets("High-Speed Rail");
// 正しい代理経由
auto* proxy = ticket->acquireProxy();
proxy->sell(proxy);
proxy->pricing(proxy);
// 不正な代理(新規生成されたもの)
auto* fakeProxy = new Proxy(ticket);
fakeProxy->sell(fakeProxy);
fakeProxy->pricing(fakeProxy);
// 本体オブジェクトからの直接呼出し(代理経由でないため拒否される)
ticket->sell(nullptr);
ticket->pricing(nullptr);
delete fakeProxy;
delete ticket;
return 0;
}
Creating proxy... Selling ticket: High-Speed Rail Price: ¥100 Access denied: invalid proxy Access denied: invalid proxy Access denied: invalid proxy Access denied: invalid proxy
この実装では、代理として唯一有効なオブジェクトを本体内に保持し、外部からの代 Convoy の照合チェックのみを通じたメソッド実行を強制しています。他の方法(別途生成した代理、null引数による本体呼び出し)はすべて拒否されます。
拡張案として、参考までに以下のようなアプローチが考えられます:
- 非決定的な識別子(タプルやハッシュ)を用いた高速な比較による多重代理対応
- 参照カウントによる代理の寿命管理
- 事前登録されたロール権限と代理の静的绑定による静的検証
ただし、上記はそれぞれ追加の複雑性を伴うため、実際の業務要件に合わせて慎重に採用する必要があります。