文章

设计模式实战:透彻解析代理模式

模式定义

代理模式(Proxy Pattern)是一种结构型设计模式,通过代理对象控制对原始对象的访问。代理在客户端和目标对象之间充当中介,可增强目标对象的功能或控制访问权限。

核心思想

  1. 访问控制:代理对象作为守门人控制对真实对象的访问

  2. 功能增强:在不修改原对象的基础上添加额外功能

  3. 延迟加载:推迟高开销对象的初始化到真正需要时

适用场景

  • 远程服务代理(如RPC调用)

  • 虚拟代理(延迟加载大资源)

  • 保护代理(权限控制)

  • 智能引用(对象访问统计)

  • 缓存代理(请求结果缓存)

模式结构

  • Subject:定义真实对象和代理的公共接口

  • RealSubject:真实业务对象

  • Proxy:代理对象,持有真实对象引用

PHP实现示例:图片加载代理

<?php
// 图片接口
interface Image {
    public function display();
}

// 真实图片类
class RealImage implements Image {
    private $filename;

    public function __construct(string $filename) {
        $this->filename = $filename;
        $this->loadFromDisk();
    }

    private function loadFromDisk() {
        echo "加载大尺寸图片: {$this->filename}\n";
    }

    public function display() {
        echo "显示图片: {$this->filename}\n";
    }
}

// 图片代理类
class ProxyImage implements Image {
    private $realImage;
    private $filename;

    public function __construct(string $filename) {
        $this->filename = $filename;
    }

    public function display() {
        if (!$this->realImage) {
            $this->realImage = new RealImage($this->filename);
        }
        $this->realImage->display();
    }
}

// 客户端使用
$image1 = new ProxyImage("photo1.jpg");
$image2 = new ProxyImage("photo2.jpg");

// 图片仅在首次显示时加载
$image1->display(); // 真实加载
$image1->display(); // 直接显示
$image2->display(); // 真实加载

Go实现示例:API访问保护代理

package main

import "fmt"

// 服务接口
type Server interface {
	HandleRequest(url string) (int, string)
}

// 真实服务
type RealServer struct{}

func (s *RealServer) HandleRequest(url string) (int, string) {
	return 200, fmt.Sprintf("处理成功: %s", url)
}

// 保护代理
type AuthProxy struct {
	server    Server
	authToken string
}

func NewAuthProxy(token string) *AuthProxy {
	return &AuthProxy{
		server:    &RealServer{},
		authToken: token,
	}
}

func (p *AuthProxy) HandleRequest(url string) (int, string) {
	if p.authToken != "secret" {
		return 403, "未授权访问"
	}
	return p.server.HandleRequest(url)
}

func main() {
	// 创建未授权的代理
	proxy1 := NewAuthProxy("guest")
	code, resp := proxy1.HandleRequest("/api/data")
	fmt.Printf("%d: %s\n", code, resp) // 403

	// 创建有效代理
	proxy2 := NewAuthProxy("secret")
	code, resp = proxy2.HandleRequest("/api/data")
	fmt.Printf("%d: %s\n", code, resp) // 200
}

模式优缺点

优点

  • 职责清晰,符合单一职责原则

  • 扩展性强,无需修改原对象

  • 控制对象生命周期

  • 支持分布式和复杂对象管理

缺点

  • 可能引入额外性能开销

  • 增加系统复杂度

  • 请求响应可能延迟(代理处理时间)

不同语言实现差异

特性

PHP

Go

接口定义

显式interface声明

隐式接口实现

代理类型

类继承/接口实现

结构体组合

访问控制

类可见性修饰符

通过包级别访问控制

典型应用

延迟加载、ORM代理

中间件、RPC代理

代理模式变种

  1. 虚拟代理:延迟创建开销大的对象(如PHP示例)

  2. 保护代理:控制访问权限(如Go示例)

  3. 远程代理:处理远程服务调用

  4. 缓存代理:存储请求结果

  5. 智能引用代理:添加对象生命周期管理

总结

代理模式通过引入代理层,实现了对原始对象的访问控制和功能增强。在需要添加间接层进行访问控制、延迟加载或功能扩展的场景下,该模式能有效解耦客户端与真实对象。不同语言在实现方式上各有特点:PHP更倾向于使用接口和类继承,而Go则通过结构体组合实现更灵活的代理机制。

实际开发中需要注意:

  • 明确代理的职责边界

  • 避免过度使用导致层级过深

  • 关注代理带来的性能影响

  • 区分代理模式与装饰器模式(关注点不同)

掌握代理模式的核心在于理解"间接访问"的思想,根据具体需求选择合适的代理类型,构建灵活可控的对象访问体系。

License:  CC BY 4.0