Golang 设计模式之代理模式

简介

代理模式(Proxy Pattern) 是一种结构型设计模式,允许我们提供一个代理对象来控制对另一个对象的访问。代理对象通常负责控制访问权限、延迟初始化或提供额外功能,而不直接暴露实际对象的细节。代理模式在实际开发中非常有用,尤其是在需要控制对象的访问、进行缓存、延迟加载或进行安全控制的场景下。

应用场景

代理模式主要应用于以下场景:

  1. 远程代理:用于处理访问远程对象的请求,比如分布式系统中的RPC调用。
  2. 虚拟代理:用于延迟创建对象,以提高性能。常用于对象的惰性加载。
  3. 保护代理:用于控制对象的访问权限,确保只有合法用户可以访问对象。
  4. 缓存代理:用于缓存耗时的操作结果,避免重复计算或请求,提升系统性能。

实际应用场景

假设你正在开发一个图片查看应用。在这个应用中,加载高清图片是一个耗时的操作。如果每次查看图片都要重新加载高清图片,会导致用户体验下降。因此,可以使用代理模式来延迟加载图片,只有在真正需要时才加载图片。

示例代码

图片接口和实际图片类

package main

import "fmt"

// Image 是图片接口
type Image interface {
    Display() // 展示图片
}

// RealImage 是实际的图片类,实现了 Image 接口
type RealImage struct {
    fileName string
}

// NewRealImage 是 RealImage 的构造函数,负责加载图片
func NewRealImage(fileName string) *RealImage {
    fmt.Println("Loading image:", fileName)
    return &RealImage{fileName: fileName}
}

// Display 是 RealImage 的展示方法
func (image *RealImage) Display() {
    fmt.Println("Displaying image:", image.fileName)
}

代理图片类

// ProxyImage 是代理图片类,代理了 RealImage 的功能
type ProxyImage struct {
    realImage *RealImage
    fileName  string
}

// NewProxyImage 是 ProxyImage 的构造函数
func NewProxyImage(fileName string) *ProxyImage {
    return &ProxyImage{fileName: fileName}
}

// Display 是 ProxyImage 的展示方法,只有在需要时才创建 RealImage
func (proxy *ProxyImage) Display() {
    if proxy.realImage == nil {
        proxy.realImage = NewRealImage(proxy.fileName) // 延迟加载图片
    }
    proxy.realImage.Display()
}

使用示例

func main() {
    // 不使用代理直接加载图片
    fmt.Println("Without Proxy:")
    realImage := NewRealImage("test_image.jpg")
    realImage.Display()
    fmt.Println()

    // 使用代理延迟加载图片
    fmt.Println("With Proxy:")
    proxyImage := NewProxyImage("test_image.jpg")
    proxyImage.Display() // 第一次调用时加载并展示图片
    proxyImage.Display() // 第二次调用时直接展示图片,不再加载
}

代码解释

  • Image 接口:定义了一个通用的图片接口,包含 Display 方法。
  • RealImage 类:实现了 Image 接口,表示实际图片类,包含 fileName 字段,负责加载并展示图片。
  • ProxyImage 类:也是 Image 的实现类,但它作为 RealImage 的代理,控制 RealImage 的访问。代理类延迟加载图片,只有在需要时才加载真实的图片。
  • main 函数:展示了使用代理模式进行图片加载的过程,第一次调用 Display 方法时会加载图片,之后的调用则不会再次加载。

图解

+-------------+        +----------------+        +-------------------+
|   Client    | -----> |   ProxyImage    | -----> |    RealImage       |
+-------------+        +----------------+        +-------------------+

在这个图中,客户端通过 ProxyImage 访问 RealImage,而 ProxyImage 控制了 RealImage 的创建和访问。

单元测试

以下是针对代理模式的单元测试,确保代理类能够正确代理实际对象。

package main

import (
    "testing"
)

func TestRealImage(t *testing.T) {
    realImage := NewRealImage("test_image.jpg")
    result := realImage.fileName

    expected := "test_image.jpg"
    if result != expected {
        t.Errorf("Expected %s but got %s", expected, result)
    }
}

func TestProxyImage(t *testing.T) {
    proxyImage := NewProxyImage("test_image.jpg")

    // 第一次调用时应该创建 RealImage
    if proxyImage.realImage != nil {
        t.Error("Expected realImage to be nil before Display()")
    }

    proxyImage.Display() // 调用 Display,加载图片
    if proxyImage.realImage == nil {
        t.Error("Expected realImage to be created after Display()")
    }
}

总结

代理模式 提供了一种通过代理对象控制对目标对象访问的方法。代理模式非常适合在以下场景中使用:

  • 远程对象访问:代理模式可以用于处理远程对象,减少直接访问远程服务的复杂性。
  • 资源管理:可以用于延迟加载资源,避免不必要的资源占用。
  • 权限控制:代理模式可以控制对象的访问权限,确保只有合法用户才能访问资源。

优点:

  • 控制对象访问:代理模式允许你控制对实际对象的访问,提供额外的功能(如缓存、权限检查)。
  • 延迟加载:代理模式可以延迟创建实际对象,只有在需要时才创建,节省资源。
  • 增强功能:代理模式可以在不修改原始类的前提下为类添加额外的功能。

缺点:

  • 增加代码复杂性:引入代理类会增加代码的复杂性,尤其是在需要管理多个代理对象时。
  • 性能开销:代理模式可能会带来一定的性能开销,因为每次调用都需要经过代理对象。

代理模式是非常灵活且强大的设计模式,适用于延迟加载、权限控制和资源管理等场景。通过代理,开发者可以为对象的访问提供额外的控制和优化,同时保持客户端代码的简洁和一致性。

本文由 mdnice 多平台发布

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UUID_UI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值