我们在Decorator设计模式的时候看到可以使用不同的方法来增强一个对象的功能. Proxy模式也一样, 但是它的目的通常是在尽可能地保留原有API的功能的基础上提供内部的功能增强.

Proxy并不是一种同质(homogeneous)API. 因为人们建立了各种不同的proxy来实现不同的目的. 在本章中我们会选择性地介绍一些, 你可以在网络上看到更多的proxy.

12.1 Smart Pointers

最简单直观的Proxy模式的使用是智能指针. 智能指针是对指针的封装, 它会持有引用计数, 重载某些操作, 但是, 总体来说, 会提供和普通指针一样的操作接口.

1
2
3
4
5
6
7
8
9
10
struct BandAccount
{
void deposit(int amount) {...}
};

BandAccount *ba = new BankAccount;
ba->deposit(124);

auto ba2 = make_shared<BandAccount>();
ba2->deposit(123); // same API!

12.1 Property Proxy

在其他的一些语言中, 名词property指代一个字段(field)和对字段的getter/setter方法. 在C++中没有属性, 但是我们可以建立一个property proxy来实现这个功能.

本质上说, property proxy是一个伪装成属性的类.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template <typename T>
struct Property
{
T value;
Property(const T initial_value)
{
*this = initial_value;
}

operator T()
{
// 实现getter操作
return value;
}

T operator =(T new new_value)
{
// 实现setter操作
return value = new_value;
}
}

这样, Property<T>是T的一个替代品. 比如, 我们这样使用它:

1
2
3
4
5
struct Creature
{
Property<int> strength {10};
Property<int> agility {5};
};

属性的使用:

1
2
3
4
Creature creature;
creature.agility = 20;

auto x = creature.strength;

12.2 Virtual Proxy

如果你试图解引用(dereference)一个nullptr或一个未初始化的指针, 就会出问题. 但是很多时候, 我们并不希望对象被过早的创建, 而只是在被访问时才创建.

这种做法被称为lazy instantiation. 接下来我们会创建一个proxy.

考虑一个典型的Image接口

1
2
3
4
struct Image
{
virtual void draw() = 0;
}

一个Bitmap的eager 实现会在它创建时立即加载数据, 即使它并不需要. 比如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
struct Bitmap : Image
{
Bitmap(const string& filename)
{
cout << "Loading image from " << filename << endl;
}

void draw() override
{
cout << "Drawing image" << filename << endl;
}
};

只要对象被创建, 就会加载图像:

1
Bitmap img{"pokemon.jpg"};  // 从pokemon.jpg中加载图像

而这并不是我们想要的. 我们想要的是只有在draw()方法被调用时才加载的类. 当然我们可以回头重新修改Bitmap的代码. 但是假如我们不能修改代码该怎么办?

我们要做的就是创建一个virtual proxy, 它包含了原先的Bitmap, 提供了相同的接口, 仍然能够被原先使用Bitmap的程序使用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct LazyBitmap : Image
{
LazyBitmap(const string& filename)
: filename(filename) {}
~LazyBitmap() { delete bmp; }
void draw() override
{
if( !bmp){
bmp = new Bitmap(filename);
}
bmp->draw();
}

private:
Bitmap *bmp{nullptr};
string filename;
};

12.3 Communication Proxy