強制代理パターン:eya绕過防止とオブジェクト制御の実装

代理パターンの基本実装では、クライアントが直接$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引数による本体呼び出し)はすべて拒否されます。

拡張案として、参考までに以下のようなアプローチが考えられます:

  • 非決定的な識別子(タプルやハッシュ)を用いた高速な比較による多重代理対応
  • 参照カウントによる代理の寿命管理
  • 事前登録されたロール権限と代理の静的绑定による静的検証

ただし、上記はそれぞれ追加の複雑性を伴うため、実際の業務要件に合わせて慎重に採用する必要があります。

タグ: proxy-pattern runtime-validation object-control cpp

7月4日 16:19 投稿