代理模式

代理模式(Proxy)

之前学习装饰器模式的时候,学到了强化功能的不同方法。代理模式也类似,但其目标是正在提供某些内部强化功能的同时准确(或尽可能地)保留正在使用的API。

代理模式没有什么同治的模式,因为不同的人构建的不同类型的代理相当多,并且用于不同目的。这里介绍一些不同的代理对象。

智能指针

最典型的STL给予我们的例子就是智能指针。智能指针是一个包装类,它封装了原始指针并维护引用计数,同时还重载了部分运算符。但总的来说智能指针提供了原始指针所具有的接口,所以在原始指针出现的位置,都可以使用智能指针。

属性代理

属性代理需要详细地介绍一下,这在游戏引擎中是常用技巧。

在其他语言中我们常常用 “属性” 表示底层成员与该成员的 getter/setter 方法的组合。但是 C++ 中没有内置属性的支持,最常见的就是创建对于该成员的 get/set 方法。但是这同时也意味着当我们要操作 t.foo 时,我们需要分别调用 t.get_foo() 和 t.set_foo(value)。但是当我们想继续使用 t.foo 的方法时,并为其提供特定的访问器 / 修改器时,我们可以构建一个 属性代理

属性代理说白了就是可以根据使用语义伪装为普通成员的类,如下定义:

template<typename T>
struct Property
{
    T value;
    Property(const T initial_value)
    {
        *this = initial_value;
    }

    operator T()
    {
        return value;
    }

    T operator=(T new_value)
    {
        return value = new_value;
    }
};

如此我们就可以如此使用这个属性代理:

struct Creature
{
    Property<int> strength{ 10 };
    Property<int> agility{ 5 };
};

Creature creature;
creature.agility = 20;
auto x = creature.strength;

属性代理的一个可能扩展是引入伪强类型,可以使用property<T, int Tag>,以便3使用不同类型定义具有不同作用的值,例如,如果我们希望在相似的类型上支持某种算法,以便可以将两个强度值相加,但强度值和敏捷度不能相加,那么这种方法非常有用。

所以下面我们将介绍一下使用标签分发(Tag Dispatch)技术来创建强类型别名(Strong Type Aliases),以避免类型混淆和逻辑错误伪强类型。

问题背景:弱类型的困境

// ❌ 问题:所有属性都是 int,容易混淆
class Character
{
public:
    int strength;     // 力量
    int agility;      // 敏捷
    int intelligence; // 智力
    int health;       // 生命值
    int mana;         // 魔法值
    
    Character(int str, int agi, int intel, int hp, int mp)
        : strength(str), agility(agi), intelligence(intel)
        , health(hp), mana(mp)
    {}
};

// ❌ 这些操作在语法上都是合法的,但在逻辑上是错误的
void demonstrateProblem()
{
    Character hero(10, 15, 8, 100, 50);
    
    // 问题1:参数顺序错误,编译器无法检测
    Character villain(100, 50, 10, 15, 8);  // 参数顺序错了!
    
    // 问题2:不同语义的值可以相加
    int total = hero.strength + hero.agility;  // 力量+敏捷?这没意义!
    
    // 问题3:可以将力量值赋给生命值
    hero.health = hero.strength;  // 类型相同,但语义不同
    
    // 问题4:函数参数容易传错
    auto improveStats = [](int& str, int& agi) {
        str += 5;
        agi += 3;
    };
    
    // 不小心传反了,编译器不会报错
    improveStats(hero.agility, hero.strength);  // ❌ 参数顺序错了
    
    std::cout << "All operations compiled successfully, but logic is wrong!\n";
}

解决方法:使用标签创建强类型

#include <iostream>
#include <string>
#include <type_traits>
#include <stdexcept>

// ============ 标签定义 ============
// 用于区分不同语义的类型

struct StrengthTag {};      // 力量标签
struct AgilityTag {};       // 敏捷标签
struct IntelligenceTag {};  // 智力标签
struct HealthTag {};        // 生命值标签
struct ManaTag {};          // 魔法值标签

// ============ 强类型属性模板 ============
// T: 底层类型(如 int, double)
// Tag: 标签类型,用于区分不同的语义

template<typename T, typename Tag>
class StrongProperty
{
private:
    T value;
    std::string name;
    
public:
    // 构造函数
    explicit StrongProperty(const std::string& propName = "unnamed", 
                           const T& initialValue = T())
        : value(initialValue), name(propName)
    {
        std::cout << "StrongProperty<" << typeid(Tag).name() << "> '" 
                  << name << "' = " << value << "\n";
    }
    
    // ✅ 只能从相同标签类型赋值
    StrongProperty& operator=(const StrongProperty<T, Tag>& other)
    {
        value = other.value;
        std::cout << "[ASSIGN] " << name << " = " << value << "\n";
        return *this;
    }
    
    // ❌ 不能从不同标签类型赋值(编译错误)
    template<typename OtherTag>
    StrongProperty& operator=(const StrongProperty<T, OtherTag>& other) = delete;
    
    // ✅ 可以从原始类型赋值
    StrongProperty& operator=(const T& val)
    {
        value = val;
        std::cout << "[SET] " << name << " = " << value << "\n";
        return *this;
    }
    
    // 获取值
    const T& get() const { return value; }
    
    // 类型转换
    operator T() const { return value; }
    
    // ✅ 相同标签类型可以比较
    bool operator==(const StrongProperty<T, Tag>& other) const
    {
        return value == other.value;
    }
    
    bool operator<(const StrongProperty<T, Tag>& other) const
    {
        return value < other.value;
    }
    
    bool operator>(const StrongProperty<T, Tag>& other) const
    {
        return value > other.value;
    }
    
    // ❌ 不同标签类型不能比较(编译错误)
    template<typename OtherTag>
    bool operator==(const StrongProperty<T, OtherTag>& other) const = delete;
    
    template<typename OtherTag>
    bool operator<(const StrongProperty<T, OtherTag>& other) const = delete;
    
    // ✅ 相同标签类型可以相加(返回新的强类型值)
    StrongProperty<T, Tag> operator+(const StrongProperty<T, Tag>& other) const
    {
        StrongProperty<T, Tag> result(name + "+" + other.name, value + other.value);
        std::cout << "[ADD] " << name << "(" << value << ") + " 
                  << other.name << "(" << other.value << ") = " 
                  << result.value << "\n";
        return result;
    }
    
    // ✅ 可以加上原始类型
    StrongProperty<T, Tag> operator+(const T& val) const
    {
        return StrongProperty<T, Tag>(name, value + val);
    }
    
    // ❌ 不同标签类型不能相加(编译错误)
    template<typename OtherTag>
    StrongProperty<T, Tag> operator+(const StrongProperty<T, OtherTag>& other) const = delete;
    
    // 自增/自减
    StrongProperty& operator+=(const StrongProperty<T, Tag>& other)
    {
        value += other.value;
        std::cout << "[+=] " << name << " now = " << value << "\n";
        return *this;
    }
    
    StrongProperty& operator+=(const T& val)
    {
        value += val;
        std::cout << "[+=] " << name << " now = " << value << "\n";
        return *this;
    }
    
    // 输出
    friend std::ostream& operator<<(std::ostream& os, 
                                   const StrongProperty<T, Tag>& prop)
    {
        os << prop.value;
        return os;
    }
};

// ============ 类型别名定义 ============
// 为了方便使用,定义具体的类型别名

using Strength = StrongProperty<int, StrengthTag>;
using Agility = StrongProperty<int, AgilityTag>;
using Intelligence = StrongProperty<int, IntelligenceTag>;
using Health = StrongProperty<int, HealthTag>;
using Mana = StrongProperty<int, ManaTag>;

使用强类型属性

#include "strong_typed_property.h"

class StrongCharacter
{
public:
    Strength strength;
    Agility agility;
    Intelligence intelligence;
    Health health;
    Mana mana;
    
    StrongCharacter(int str, int agi, int intel, int hp, int mp)
        : strength("strength", str)
        , agility("agility", agi)
        , intelligence("intelligence", intel)
        , health("health", hp)
        , mana("mana", mp)
    {}
    
    // ✅ 只接受力量类型的函数
    void increaseStrength(const Strength& bonus)
    {
        strength += bonus;
    }
    
    // ✅ 只接受敏捷类型的函数
    void increaseAgility(const Agility& bonus)
    {
        agility += bonus;
    }
    
    void printStats() const
    {
        std::cout << "\n=== Character Stats ===\n";
        std::cout << "Strength: " << strength << "\n";
        std::cout << "Agility: " << agility << "\n";
        std::cout << "Intelligence: " << intelligence << "\n";
        std::cout << "Health: " << health << "\n";
        std::cout << "Mana: " << mana << "\n";
    }
};

int main()
{
    std::cout << "=== Creating Strong Typed Character ===\n";
    StrongCharacter hero(10, 15, 8, 100, 50);
    
    hero.printStats();
    
    std::cout << "\n=== Valid Operations ===\n";
    
    // ✅ 相同类型可以相加
    Strength bonus1("bonus1", 5);
    Strength bonus2("bonus2", 3);
    Strength totalBonus = bonus1 + bonus2;
    std::cout << "Total strength bonus: " << totalBonus << "\n";
    
    // ✅ 可以增加相同类型的值
    hero.strength += bonus1;
    
    // ✅ 相同类型可以比较
    if (hero.strength > Strength("threshold", 10))
    {
        std::cout << "Hero is strong!\n";
    }
    
    // ✅ 可以赋值相同类型
    Strength newStrength("newStrength", 20);
    hero.strength = newStrength;
    
    std::cout << "\n=== Invalid Operations (Compile Errors) ===\n";
    std::cout << "The following would cause compile errors:\n";
    
    // ❌ 编译错误:不能将力量加到敏捷上
    // auto invalid1 = hero.strength + hero.agility;
    std::cout << "// auto invalid1 = hero.strength + hero.agility;  // ❌ Compile Error\n";
    
    // ❌ 编译错误:不能将力量赋值给生命值
    // hero.health = hero.strength;
    std::cout << "// hero.health = hero.strength;  // ❌ Compile Error\n";
    
    // ❌ 编译错误:不能比较力量和敏捷
    // if (hero.strength > hero.agility) {}
    std::cout << "// if (hero.strength > hero.agility) {}  // ❌ Compile Error\n";
    
    // ❌ 编译错误:函数参数类型不匹配
    // hero.increaseStrength(hero.agility);
    std::cout << "// hero.increaseStrength(hero.agility);  // ❌ Compile Error\n";
    
    // ❌ 编译错误:不能将敏捷加到力量上
    // hero.strength += hero.agility;
    std::cout << "// hero.strength += hero.agility;  // ❌ Compile Error\
    \n";
    
    hero.printStats();
    
    return 0;
}

支持特定操作的强类型

#include <iostream>
#include <string>
#include <type_traits>

// ============ 特性标签 ============
// 用于控制哪些操作是允许的

struct Addable {};      // 可加
struct Subtractable {}; // 可减
struct Multipliable {}; // 可乘
struct Comparable {};   // 可比较

// ============ 高级强类型属性 ============
template<typename T, typename Tag, typename... Traits>
class AdvancedProperty
{
private:
    T value;
    std::string name;
    
    // 检查是否具有某个特性
    template<typename Trait>
    static constexpr bool hasTrait()
    {
        return (std::is_same_v<Trait, Traits> || ...);
    }
    
public:
    explicit AdvancedProperty(const std::string& propName = "unnamed",
                             const T& initialValue = T())
        : value(initialValue), name(propName)
    {}
    
    const T& get() const { return value; }
    operator T() const { return value; }
    
    AdvancedProperty& operator=(const T& val)
    {
        value = val;
        return *this;
    }
    
    // ✅ 只有具有 Addable 特性才能相加
    template<typename U = void>
    std::enable_if_t<hasTrait<Addable>(), AdvancedProperty>
    operator+(const AdvancedProperty& other) const
    {
        std::cout << "[ADD] " << name << "(" << value << ") + " 
                  << other.name << "(" << other.value << ")\n";
        return AdvancedProperty(name + "+" + other.name, value + other.value);
    }
    
    template<typename U = void>
    std::enable_if_t<hasTrait<Addable>(), AdvancedProperty&>
    operator+=(const AdvancedProperty& other)
    {
        value += other.value;
        std::cout << "[+=] " << name << " now = " << value << "\n";
        return *this;
    }
    
    // ✅ 只有具有 Subtractable 特性才能相减
    template<typename U = void>
    std::enable_if_t<hasTrait<Subtractable>(), AdvancedProperty>
    operator-(const AdvancedProperty& other) const
    {
        std::cout << "[SUB] " << name << "(" << value << ") - " 
                  << other.name << "(" << other.value << ")\n";
        return AdvancedProperty(name + "-" + other.name, value - other.value);
    }
    
    template<typename U = void>
    std::enable_if_t<hasTrait<Subtractable>(), AdvancedProperty&>
    operator-=(const AdvancedProperty& other)
    {
        value -= other.value;
        std::cout << "[-=] " << name << " now = " << value << "\n";
        return *this;
    }
    
    // ✅ 只有具有 Multipliable 特性才能相乘
    template<typename U = void>
    std::enable_if_t<hasTrait<Multipliable>(), AdvancedProperty>
    operator*(const T& scalar) const
    {
        std::cout << "[MUL] " << name << "(" << value << ") * " << scalar << "\n";
        return AdvancedProperty(name + "*" + std::to_string(scalar), 
                               value * scalar);
    }
    
    // ✅ 只有具有 Comparable 特性才能比较
    template<typename U = void>
    std::enable_if_t<hasTrait<Comparable>(), bool>
    operator>(const AdvancedProperty& other) const
    {
        return value > other.value;
    }
    
    template<typename U = void>
    std::enable_if_t<hasTrait<Comparable>(), bool>
    operator<(const AdvancedProperty& other) const
    {
        return value < other.value;
    }
    
    template<typename U = void>
    std::enable_if_t<hasTrait<Comparable>(), bool>
    operator==(const AdvancedProperty& other) const
    {
        return value == other.value;
    }
    
    friend std::ostream& operator<<(std::ostream& os, 
                                   const AdvancedProperty& prop)
    {
        os << prop.value;
        return os;
    }
};

// ============ 具体类型定义 ============

// 力量:可加、可比较
using AdvStrength = AdvancedProperty<int, StrengthTag, Addable, Comparable>;

// 敏捷:可加、可比较
using AdvAgility = AdvancedProperty<int, AgilityTag, Addable, Comparable>;

// 伤害:可加、可减、可乘(伤害计算)、可比较
using Damage = AdvancedProperty<int, struct DamageTag, 
                                Addable, Subtractable, Multipliable, Comparable>;

// 防御:可减(减少伤害)、可比较
using Defense = AdvancedProperty<int, struct DefenseTag, 
                                 Subtractable, Comparable>;

// 经验值:只能加、可比较
using Experience = AdvancedProperty<int, struct ExperienceTag, 
                                    Addable, Comparable>;

// ID:只能比较(不能进行算术运算)
using EntityID = AdvancedProperty<int, struct EntityIDTag, Comparable>;

使用高级强类型:

#include "advanced_strong_property.h"

class GameEntity
{
public:
    EntityID id;
    AdvStrength strength;
    AdvAgility agility;
    Experience exp;
    
    GameEntity(int entityId, int str, int agi, int experience)
        : id("id", entityId)
        , strength("strength", str)
        , agility("agility", agi)
        , exp("exp", experience)
    {}
};

class CombatSystem
{
public:
    // 计算伤害
    static Damage calculateDamage(const AdvStrength& str, int multiplier)
    {
        Damage baseDamage("baseDamage", str.get());
        
        // ✅ Damage 支持乘法
        Damage finalDamage = baseDamage * multiplier;
        
        std::cout << "Final damage: " << finalDamage << "\n";
        return finalDamage;
    }
    
    // 应用防御
    static Damage applyDefense(const Damage& damage, const Defense& defense)
    {
        // ✅ Damage 支持减法
        Damage reducedDamage("reducedDamage", damage.get());
        
        // 注意:这里需要手动计算,因为 Damage 和 Defense 是不同类型
        int finalValue = damage.get() - defense.get();
        if (finalValue < 0) finalValue = 0;
        
        std::cout << "Damage after defense: " << finalValue << "\n";
        return Damage("finalDamage", finalValue);
    }
    
    // 增加经验
    static void gainExperience(Experience& currentExp, const Experience& gained)
    {
        // ✅ Experience 支持加法
        currentExp += gained;
    }
};

int main()
{
    std::cout << "=== Creating Game Entities ===\n";
    GameEntity player(1001, 50, 30, 0);
    GameEntity enemy(2001, 40, 25, 0);
    
    std::cout << "\n=== Valid Operations ===\n";
    
    // ✅ 力量可以相加
    AdvStrength strengthBonus("bonus", 10);
    player.strength += strengthBonus;
    
    // ✅ 力量可以比较
    if (player.strength > enemy.strength)
    {
        std::cout << "Player is stronger!\n";
    }
    
    // ✅ 经验值可以增加
    Experience expGained("expGained", 100);
    CombatSystem::gainExperience(player.exp, expGained);
    
    // ✅ ID 可以比较
    if (player.id == EntityID("playerId", 1001))
    {
        std::cout << "Player ID matched!\n";
    }
    
    // ✅ 伤害计算
    Damage damage = CombatSystem::calculateDamage(player.strength, 2);
    
    // ✅ 应用防御
    Defense enemyDefense("enemyDefense", 30);
    Damage finalDamage = CombatSystem::applyDefense(damage, enemyDefense);
    
    std::cout << "\n=== Invalid Operations (Would Cause Compile Errors) ===\n";
    std::cout << "The following operations are not allowed:\n";
    
    // ❌ 力量不能相减(没有 Subtractable 特性)
    // auto invalid1 = player.strength - enemy.strength;
    std::cout << "// auto invalid1 = player.strength - enemy.strength;  // ❌\n";
    
    // ❌ ID 不能相加(没有 Addable 特性)
    // auto invalid2 = player.id + enemy.id;
    std::cout << "// auto invalid2 = player.id + enemy.id;  // ❌\n";
    
    // ❌ 经验值不能相减(没有 Subtractable 特性)
    // auto invalid3 = player.exp - expGained;
    std::cout << "// auto invalid3 = player.exp - expGained;  // ❌\n";
    
    // ❌ 力量不能相乘(没有 Multipliable 特性)
    // auto invalid4 =  player.strength * 2;
    std::cout << "// auto invalid4 = player.strength * 2;  // ❌\n";
    
    // ❌ 不同类型不能混合运算
    // auto invalid5 = player.strength + player.agility;
    std::cout << "// auto invalid5 = player.strength + player.agility;  // ❌\n";
    
    return 0;
}

虚拟代理

虚拟代理的核心思想和单例模式中的懒汉模式类似,在需要使用到资源时再创建。这是为了在加载开销大的资源时能够延迟创建它。

问题:某些对象创建成本很高
┌─────────────────────────────────────────┐
  加载大文件(图片、视频、文档)         
  建立数据库连接                         
  初始化复杂的数据结构                   
  网络资源获取                           
└─────────────────────────────────────────┘
                    
解决方案:虚拟代理 = 占位符 + 延迟加载
┌─────────────────────────────────────────┐
 1. 创建轻量级代理对象(立即)            
 2. 只在真正需要时才创建真实对象          
 3. 对客户端透明                          
└─────────────────────────────────────────┘

示例

假设我们有一个加载图片的需求:

class Image
{
public:
    virtual void display() = 0;
    ~Image() = default;
};

class Bitmap : public Image
{
public:
    Bitmap(const std::string& filename)
    {
        std::cout << "loading image from" << filename << "\n";
        // loading image here
    }
    void display() override
    {
        // display image
    }
};

那么当我们创建 Bitmap 时将直接触发加载图片的行为:

Bitmap img{"picture.png"};

但我们想提前创建一个占位,当我们真正需要使用到这张图片的时候再加载它。

此时我们可以创建一个虚拟代理:

class LazyBitmap : public image
{
private:
    Bitmap* bmp{nullptr};
    std::string filename;
public:
    LazyBitmap(const std::string& filename)
        :filename(filename)
    {}
    ~LazyBitmap() {delete bmp;}
    void display() override
    {
        if(!bmp)
            bmp = new Bitmap(filename);
        bmp->display();
    }
};

如上,我们就能在接口需要某个 image 时传入 LazyBitmap 以实现延迟加载了。

通信代理

通信代理很像装饰器模式,但其本质的特点在于装饰器模式操作的是在本地的对象,而通信代理操作的很可能是远程对象,其数据的获取需要通过网络通信:

┌──────────────┬─────────────────────┬─────────────────────┐
│              │      装饰器         │     通信代理        │
├──────────────┼─────────────────────┼─────────────────────┤
│ 对象位置     │ 本地(同一进程)     │ 远程(不同机器)    │
├──────────────┼─────────────────────┼─────────────────────┤
│ 调用方式     │ 直接方法调用        │ 网络/IPC通信         │
├──────────────┼─────────────────────┼─────────────────────┤
│ 主要目的     │ 增强功能            │ 隐藏远程性           │
├──────────────┼─────────────────────┼─────────────────────┤
│ 客户端感知   │ 知道是装饰          │ 不知道是远程         │
├──────────────┼─────────────────────┼─────────────────────┤
│ 性能开销     │ 几乎无              │ 网络延迟             │
├──────────────┼─────────────────────┼─────────────────────┤
│ 典型例子     │ 日志、缓存、压缩    │ RPC、Web服务         │
└──────────────┴─────────────────────┴─────────────────────┘

这里提供一个对比示例:

示例

#include <iostream>
#include <string>

// ============ 装饰器:本地文件操作 ============
class FileWriter
{
public:
    virtual void write(const std::string& data) 
    {
        std::cout << "写入文件: " << data << "\n";
    }
};

// 装饰器:添加压缩功能
class CompressedFileWriter : public FileWriter
{
private:
    FileWriter* writer;  // 本地对象
public:
    CompressedFileWriter(FileWriter* w) : writer(w) {}
    
    void write(const std::string& data) override
    {
        std::cout << "[装饰] 压缩数据...\n";
        writer->write(data);  // 直接调用,本地操作
    }
};

// ============ 通信代理:远程数据库 ============
class Database
{
public:
    virtual ~Database() = default;
    virtual std::string query(const std::string& sql) = 0;
};

// 真实数据库在远程服务器上
class RemoteDatabase : public Database
{
public:
    std::string query(const std::string& sql) override
    {
        std::cout << "    [数据库服务器] 执行SQL: " << sql << "\n";
        return "查询结果";
    }
};

// 通信代理
class DatabaseProxy : public Database
{
public:
    std::string query(const std::string& sql) override
    {
        std::cout << "  [代理] 连接 db.server.com:3306\n";
        std::cout << "  [代理] 发送SQL查询...\n";
        
        // 实际通过网络调用远程数据库
        RemoteDatabase remoteDB;
        std::string result = remoteDB.query(sql);
        
        std::cout << "  [代理] 接收结果\n";
        return result;
    }
};

int main()
{
    std::cout << "=== 实际应用对比 ===\n\n";
    
    std::cout << "【装饰器】本地文件写入:\n";
    FileWriter writer;
    CompressedFileWriter compWriter(&writer);
    compWriter.write("Hello World");
    std::cout << "→ 文件和程序在同一台机器\n\n";
    
    std::cout << "【通信代理】远程数据库查询:\n";
    DatabaseProxy db;
    std::string result = db.query("SELECT * FROM users");
    std::cout << "结果: " << result << "\n";
    std::cout << "→ 数据库在另一台服务器上\n";
    
    return 0;
}

值代理

2.3.3 设计元素描述

1. 表示层组件

经济损失统计页面 (LossPage)

  • 职责:展示火灾造成的经济损失统计数据
  • 功能:
    • 显示直接经济损失和间接经济损失
    • 按月度、季度、年度统计
    • 损失对比分析
    • 损失趋势图表
    • 数据导出功能
  • 交互:调用经济损失服务

火灾原因分析页面 (ReasonPage)

  • 职责:分析和展示火灾发生的原因统计
  • 功能:
    • 显示各类原因的统计数据(存储电池、粉尘、电气等)
    • 原因占比分析
    • 原因趋势分析
    • 饼图和柱状图展示
  • 交互:调用火灾原因服务

趋势预测页面 (TrendPage)

  • 职责:基于历史数据进行趋势预测
  • 功能:
    • 经济损失趋势预测
    • 火灾发生频率预测
    • 风险等级预测
    • 预测结果可视化
  • 交互:调用趋势预测服务

报表生成页面 (ReportPage)

  • 职责:生成和导出各类统计报表
  • 功能:
    • 选择报表类型(日报、周报、月报、年报)
    • 自定义报表时间范围
    • 报表预览
    • 导出为PDF、Excel、CSV格式
  • 交互:调用报表生成服务和数据导出服务