代理模式(结构型模式)
模式概述
代理模式是一种结构型设计模式,它能够提供对象的替代品或其占位符。
代理控制着对于原对象的访问,并允许在将请求提交给对象前后进行一些处理。例如,Nginx所提供的反向代理服务,就是一种代理。
问题
如果一位亿万富翁带着大笔现金到处走动,一来行动肯定是非常不方便,二来也非常不安全,容易被偷或者被抢。
但如果不带这些钱他又无法顺利地和客户达成交易。
方案
为此,银行发明了储蓄卡和信用卡,这就是富翁所拥有的银行账户的代理,而银行账户背后则代表着一大堆的现金。
现金和银行卡一样,都实现了支付接口,都可以用于支付。这样让三方都满意:富翁不再担心他的前会被偷或被抢;银行有了储户;富翁的客户也能顺利地做生意了。

结构

实现
Java
/**
* 支付服务接口
*
*/
public interface Payment {
int pay(int amount);
}
/**
* 银行信用卡
*
*/
public class CreditCard implements Payment {
private Cash cash;
public CreditCard(int totalAmount) {
this.cash = new Cash(totalAmount);
}
@Override
public int pay(String amount) {
System.out.println("待支付 " + amount + " 元");
int balance = cash.pay(amount);
System.out.println("余额 " + balance + " 元");
return balance;
}
}
/**
* 现金
*
*/
public class Cash implements Payment {
// 总金额
public int totalAmount;
Cash(int totalAmount) {
this.totalAmount = totalAmount;
}
@Override
public int pay(String amount) {
return totalAmount - amount;
}
}
/**
* 客户端
*
*/
public class Client {
private Payment payment;
public Client(Payment payment) {
this.payment = payment;
}
public static void main(String[] args) {
Payment payment = new CreditCard(10000);
payment.pay(100);
}
}
Go
package main
import "fmt"
/**
* 代理请求接口
*
*/
type server interface {
handleRequest(string, string) (int, string)
}
/**
* 被代理对象
*
*/
type Application struct {
}
func (a *Application) handleRequest(url, method string) (int, string) {
if url == "/app/status" && method == "GET" {
return 200, "Ok"
}
if url == "/create/user" && method == "POST" {
return 201, "User Created"
}
return 404, "Not Ok"
}
/**
* 代理
*
*/
type Nginx struct {
application *Application
maxAllowedRequest int
rateLimiter map[string]int
}
func newNginxServer() *Nginx {
return &Nginx{
application: &Application{},
maxAllowedRequest: 2,
rateLimiter: make(map[string]int),
}
}
func (n *Nginx) handleRequest(url, method string) (int, string) {
allowed := n.checkRateLimiting(url)
if !allowed {
return 403, "Not Allowed"
}
return n.application.handleRequest(url, method)
}
func (n *Nginx) checkRateLimiting(url string) bool {
if n.rateLimiter[url] == 0 {
n.rateLimiter[url] = 1
}
if n.rateLimiter[url] > n.maxAllowedRequest {
return false
}
n.rateLimiter[url] = n.rateLimiter[url] + 1
return true
}
/**
* 客户端
*
*/
func main() {
nginxServer := newNginxServer()
appStatusURL := "/app/status"
createuserURL := "/create/user"
httpCode, body := nginxServer.handleRequest(appStatusURL, "GET")
fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
httpCode, body = nginxServer.handleRequest(appStatusURL, "GET")
fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
httpCode, body = nginxServer.handleRequest(createuserURL, "POST")
fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
httpCode, body = nginxServer.handleRequest(createuserURL, "GET")
fmt.Printf("\nUrl: %s\nHttpCode: %d\nBody: %s\n", appStatusURL, httpCode, body)
}
适用场景
如果有一个偶尔使用且资源消耗较大的服务对象,可使用代理模式代替长期访问。
如果所需的服务位于远程服务器上,可使用网络代理来访问。
如果只希望特定客户端使用某个服务对象,可使用代理模式。
优缺点
代理模式的优点。
即使服务对象还未准备好或不存在, 代理也可以正常工作,减少资源或时间的消耗。
可以在不对服务做出修改的情况下添加额外服务,符合
开闭原则
。降低访问服务的耦合度。
对真实访问对象的使用权限起到保护作用。
代理模式的缺点。
服务响应可能会延迟或不稳定。
实现代理模式需要额外的工作,有些代理模式的实现较为复杂,不得不做额外的很多工作。
相关性
适配器模式
能为对象提供匹配的访问接口,代理模式
能为对象提供相同的接口,修饰器模式
则能为对象提供加强的接口。修饰器模式
与代理模式
的结构相似,都将部分工作委派给零一对象,但它们的目的不同。代理模式
通常自行管理服务对象的生命周期, 而修饰器模式
的生命周期总是由客户端来控制。代理模式
与外观模式
的相似之处在于都封装了另一个实体并对其进行初始化,但代理模式
可与服务对象互换,而外观模式
不行。
感谢支持
更多内容,请移步《超级个体》。