前言

结构型模式关注如何组合对象以形成更大的结构,提供了一种将对象组合成具有特定行为的结构的最佳方式。本章介绍创建型模式中的代理模式和桥接模式。


8. 代理模式(Proxy)

定义

代理模式是一种结构型设计模式,主要用于为其他对象提供一种代理以控制对这个对象的访问。代理模式的主要目的是在不改变原始对象的情况下,附加新功能,提供一种间接的访问方式,以实现对原始对象的控制、管理或延迟加载。

解决方案

代理模式的核心就是要创建一个代理类,这个代理类在客户端和目标对象之间起到中介的作用。代理类会持有目标对象的引用,并在合适的时候将客户端的请求转发给目标对象。同时,代理类可以在转发请求前后执行一些附加的操作。

以下是一个简单的C++代理模式实现,模拟了一个图像加载的代理,用于延迟加载图像。

// 图像延迟加载
#include <iostream>
#include <memory>
// 目标类(RealSubject)
class Image {
public:
    virtual void display() {
        std::cout << "Real Image Displayed." << std::endl;
    }
};

// 代理类(Proxy)
class ProxyImage : public Image {
private:
    std::unique_ptr<Image> realImage; // 持有真实对象的引用
    bool isLoaded = false;

public:
    void display() override {
        if (!isLoaded) {
            loadRealImage();
        }
        realImage->display();
    }

    void loadRealImage() {
        if (!isLoaded) {
            realImage = std::make_unique<Image>();
            isLoaded = true;
            std::cout << "Real Image Loaded." << std::endl;
        }
    }
};

int main() {
    ProxyImage proxyImage;
    // 第一次调用display(),会加载真实图像
    proxyImage.display();
    // 第二次调用display(),由于图像已加载,所以直接显示
    proxyImage.display();
    return 0;
}

延迟加载图像
在这个例子中,ProxyImage 类是 Image 类的代理,它延迟了真实图像对象的加载,直到 display() 方法被调用时才加载。这有助于节省资源,特别是在处理大量图像或数据密集型应用时。

应用场景

1. 远程代理: 为一个位于不同的地址空间(如网络)上的对象提供一个本地的代理对象,以隐藏远程对象实现的细节。
2. 虚拟代理: 根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象的引用。
3. 保护代理(访问控制): 控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
4. 智能指引: 当调用真实的对象时,代理处理另外一些事务。
5. 延迟加载: 在处理大量图像或数据密集型应用时,可以延迟到需要使用时再进行加载,有助于节约资源。

优缺点

优点:

  • 代理模式能够将客户端与目标对象分离,在一定程度上降低了系统的耦合度
  • 可以在客户端和目标对象之间起到中介的作用,通过代理对象来增强目标对象的功能
  • 客户端可以针对抽象编程,增加系统的灵活性

缺点:

  • 在客户端和目标对象之间增加了一个代理层,可能会导致请求的处理速度变慢
  • 增加了系统的复杂度

9. 桥接模式(Bridge)

定义

桥接模式是一种结构型设计模式,它将抽象部分与它的实现部分分离,使它们都可以独立地变化(不是通过类继承的方式,而是通过对象组合的方式)。这种模式通过创建一个抽象层来隔离接口和实现细节,从而使得两者可以独立变化。桥接模式主要用于减少类之间的耦合,提高系统的可扩展性和可维护性。

为什么使用桥接模式

1. 解耦抽象和实现: 桥接模式通过分离抽象接口及其实现,使得二者可以独立变化,提高了系统的灵活性和可扩展性。
2. 易于扩展: 增加新的抽象类或实现类时,不需要修改原有代码,只需添加新的类或实现即可。
3. 符合开闭原则: 对扩展开放,对修改关闭,增加新功能时不需要修改原有代码。

应用场景

  1. 当一个类存在两个或多个独立变化的维度,且这两个维度都需要进行扩展时。
  2. 当不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
  3. 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。
// 绘制彩色的圆
#include <iostream>  
#include <string>  
// 抽象实现类
class Color {
public:
    virtual ~Color() {}
    virtual void fill() = 0;
};
// 具体实现类
class Red : public Color {
public:
    void fill() override {
        std::cout << "Inside Red::fill()" << std::endl;
    }
};
class Blue : public Color {
public:
    void fill() override {
        std::cout << "Inside Blue::fill()" << std::endl;
    }
};

// 抽象类
class Shape {
protected:
    Color* color;
public:
    Shape(Color* c) : color(c) {}
    virtual ~Shape() { delete color; }
    void setColor(Color* c) {
        delete color;
        color = c;
    }
    virtual void draw() = 0;
};
// 具体类
class Circle : public Shape {
public:
    Circle(Color* c) : Shape(c) {}
    void draw() override {
        color->fill();
        std::cout << "Inside Circle::draw()" << std::endl;
    }
};
// 客户端代码
int main() {
    Shape* circle = new Circle(new Red());
    circle->draw();
    // 更改颜色
    circle->setColor(new Blue());
    circle->draw();

    delete circle;
    return 0;
}

这个例子中,Shape 是抽象类,代表图形,它持有一个 Color 类型的指针,用于在绘制图形时填充颜色。Color 是一个抽象类,定义了填充颜色的接口。Red 和 Blue 是 Color 的具体实现类,分别实现了不同的颜色填充方式。Circle 是 Shape 的一个具体类,实现了绘制圆形的逻辑。
该程序需要支持不同的图形(如圆形、矩形)和不同的颜色填充方式(如红色、蓝色)。使用桥接模式,可以将图形的形状和颜色填充方式分离成两个独立的层次结构,可以很容易地改变图形的颜色或添加新的图形和颜色类型,而不需要修改现有代码。

优缺点

优点:

  • 分离抽象接口及其实现部分,提高了系统的可扩展性。
  • 符合开闭原则,易于扩展和维护。
  • 提高了系统的灵活性,降低了类之间的耦合度。

缺点:

  • 增加了系统的复杂度,需要理解抽象层与实现层的分离。
  • 如果系统的抽象和实现变化不大,使用桥接模式反而会增加系统的复杂性。

To be continued.

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐