设计模式笔记——代理模式
代理模式介绍
代理模式通常是介于请求方和提供方的一个中介系统,请求方是发送请求的一方,提供方是根据请求提供相应资源的一方
Web中的代理服务器就是一个例子,客户端向代理服务器发送网业请求,连接代理服务器,代理服务器评估请求,并将封装后的请求发送到相应的远程服务器,受到响应后,就将响应发送给客户端,在这个过程中,代理服务器起到了一个中介的作用,起到一个封装请求,保护隐私的作用
非常适合在分布式架构中运行
代理模式充当实际对象接口的类,对象类型是多样化的,网络连接、内存、文件中的大对象
,代理封装实际服务对象的包装器或代理人,它可以为其包装的对象提供附加功能,而无需更改对象的代码,其主要目的就是为实际对象提供一个代理者或占位符,从而控制对象的实际访问,它有很多适应场景
- 对于一个复杂的系统,代理可以以一种简单的方式表示它,从而封装其内部的复杂性
- 能将实际对象保护起来,提高实际对象的安全性,很多情况下,都不允许客户端直接访问实际对象
- 为不同服务器上的远程对象提供本地接口
- 为消耗大量内存的远程对象提供一个轻量级句柄
PS:
当制作公司想找演员拍电影时,他们通常会与经纪人直接交流,而不是跟演员交流,经纪人会根据演员的日程安排确定演员是否有空,从而给出交涉结果,制作公司(客户端)不直接找演员(实际对象),而找其经纪人直接交流(代理Agent)
class Actor(object):
def __init__(self):
self.isBusy = False
def occupied(self):
self.isBusy = True
print(type(self).__name__, "is occupied with Current movie")
def available(self):
self.isBusy = False
print(type(self).__name__, "is free for the movie")
def getStatus(self):
return self.isBusy
class Agent(object):
def __init__(self):
self.principal = None
def work(self):
self.actor = Actor()
if self.actor.getStatus():
self.actor.occupied()
else:
self.actor.available()
if __name__ == "__main__":
r = Agent()
r.work()
- 代理模式为其他对象提供了一个代理,实现对原始对象的访问控制
- 用作一个层或接口,支持分布式访问
- 能增加代理,保护真正的组件不受意外的影响
UML类图
UML分为三个参与者组成:代理Proxy
、真实对象RealSubject
、主题Subject
,其UML组成图为:
代理:维护一个引用,允许代理(Proxy)通过这个引用来访问实际对象,提供一个与主题(Subject)相同的接口,以便代理替换真实的主题,其还负责创建和删除真实主题
主题:定义了RealSubject和Proxy的公共接口,和形式主题Subject,使用RealSubject的任何地方都可以使用代理
真实主题:它定义代理(Proxy)所代表的真实对象
而纵观整个UML图、可以看见:
**代理:**控制对RealSubject类访问的类,处理客户端的请求,负责创建和删除RealSubject
主题/真实主题:定义真实主题和代理的公共接口,真实主题提供功能的真实实现
**客户端:**它访问完成工作的Proxy类,代理内部负责对RealSubject的访问与控制,并响应客户端的请求
四种类型的代理模式
虚拟代理
如果一个对象实例化需要占用大量内存的话,可以先用一个占位符来表示,直到真的请求这个对象时,才真正的访问它,即在一个Web网站中,如果一个大型图片的加载需要耗费大量的资源,那么在未点击它之前,会先用一个占位符来表示,直到真的点击这个图片时,它才会显示出来,即创建实际对象
远程代理
给位于远程服务器或不同位置的地址空间上的实际对象提供一个本地表示,比如,希望应用程序建立一个监控系统,而该应用涉及多个Web服务器、数据库服务器、芹菜任务服务器、缓存服务器等等。如果要监视这些服务的CPU、内存和磁盘利用率,就需要建立一个对象,用于监视应用程序运行的上下文中,同时还可以执行远程命令以获取参数值。
保护代理
控制对敏感实例对象的访问,代理会在客户端发送请求时先对这个请求进行合法的验证,防止非法的请求出现导致远程对象实例的崩溃,代理者会检查调用者是否具有转发请求所需的访问权限
智能代理
访问对象时拆入其他操作,例如,假设系统中有一个核心组件,他将状态信息集中保存在一个地点,通常情况下,这样的组件需要被多个不同的服务调用以完成它们的任务,并且可能导致共享资源的问题。与让服务直接调用核心组件不同,智能代理是内置的,并且会在访问之前检查实际对象是否被锁定,确保没有其他对象更改他
实例
以一个人去商店买水为例子,现在都支持微信支付,扫码,付款,而微信就可以理解为充当了一个代理,而真正执行扣钱的是背后的银行,微信只是帮忙维护这个接口,而封装了银行具体的操作,这里假设客户端是你,You,在初始化的时候会调用代理将真实对象实例化,而通过make_pay
来付钱,并通过__del__
来给出付款结果
而对于微信WeChat,他和银行Bank都实现了主题(Payment)一个do_pay
方法,在这个过程中,Bank通过Wechat发送过来的账户信息,进行真实的扣款操作,其除了do_pay
方法之外,还有__hasEnoughMenoy
来检查余额是否充足,setAccount
来设置账户信息,__reduceBalance
来扣款,通过__checkAccount
来验证用户和用户密码是否正确,而WeChat调用Bank的do_pay
实现扣款操作
Python实现
# -*- coding=utf-8 -*-
from abc import ABCMeta, abstractmethod
BankAccountData: dict = {
"acc1":["10001", 50],
"acc2":["10002", 100]
}
class Payment(metaclass=ABCMeta):
@abstractmethod
def do_pay(self, pay: int) -> bool:
pass
class Bank(Payment):
account: str
password: str
def __init__(self):
self.account = None
self.password = None
def setAccount(self):
self.account = input("Please input the account:")
self.password = input("Please input the password:")
def __checkAccount(self) -> bool:
if self.account in BankAccountData:
if self.password == BankAccountData[self.account][0]:
return True
return False
def __reduceBalance(self, money: int) -> bool:
if self.__hasEnoughMeony(money):
BankAccountData[self.account][1] -= money
return True
return False
def __hasEnoughMeony(self, money: int) -> bool:
if self.__checkAccount():
if money <= BankAccountData[self.account][1]:
return True
return False
print("Account or Password that you input is error!")
return False
def do_pay(self, pay: int) -> bool:
return self.__reduceBalance(pay)
class WetChat(Payment):
def __init__(self):
self.bank = Bank()
def do_pay(self, pay: int) -> bool:
self.bank.setAccount()
return self.bank.do_pay(pay)
class You(object):
def __init__(self):
print("Let me buy the water!")
self.wetchat = WetChat()
self.isPurchased = None
def make_pay_water(self):
self.isPurchased = self.wetchat.do_pay(2)
def __del__(self):
if self.isPurchased:
print("Pay successfully!")
else:
print("Pay failed!")
if __name__ == "__main__":
you = You()
you.make_pay_water()
Let me buy the water!
Please input the account:acc1
Please input the password:10001
Pay successfully!
C++实现
#include<iostream>
#include<map>
#include<string>
using namespace std;
map<string, pair<string, int>> bank_account_data = {{"acc1",pair<string, int>("10001", 50)},
{"acc2",pair<string, int>("10002", 100)}};
class Payment{
public:
virtual bool do_pay(int money) = 0;
};
class Bank:public Payment{
public:
Bank(){
account = "";
password = "";
}
public:
void setAccount(){
cout<<"Please input the account:";
cin>>account;
cout<<"Please input the password:";
cin>>password;
}
bool do_pay(int money) override{
return __reduceBalance(money);
}
private:
bool __checkAccount(){
if(bank_account_data.find(account) != bank_account_data.end()){
auto res = bank_account_data[account];
if(password == res.first){
return true;
}
}
return false;
}
bool __hasEnoughMoney(int money){
if(__checkAccount()){
if(money <= bank_account_data[account].second)
return true;
return false;
}
cout<<"Account or Password that you input is error!"<<endl;
return false;
}
bool __reduceBalance(int money){
if(__hasEnoughMoney(money)){
bank_account_data[account].second -= money;
return true;
}
return false;
}
private:
string account;
string password;
};
class WetChat: public Payment{
public:
WetChat(){
bank = new Bank();
}
public:
bool do_pay(int money) override{
bank->setAccount();
return bank->do_pay(money);
}
private:
Bank *bank;
};
class You{
public:
You(){
cout<<"Let me but the water!"<<endl;
wetchat = WetChat();
isPurchased = false;
}
~You(){
if(isPurchased)
cout<<"Pay successfully!"<<endl;
else
cout<<"Pay failed!"<<endl;
}
public:
void make_pay_water(){
isPurchased = wetchat.do_pay(2);
}
private:
WetChat wetchat;
bool isPurchased;
};
int main(int argc, char *argv[]){
You you = You();
you.make_pay_water();
}
Let me but the water!
Please input the account:acc1
Please input the password:10001
Pay successfully!
代理模式的优点
- 代理可以通过缓存笨重的对象或频繁访问的对象来提高程序的性能
- 提供对于真实主题的访问特权,因此,只有提供合适权限的情况下,这个模式才会接受委派
- 远程代码还便于可用作网络连接和数据库连接的远程服务器进行交互
门面模式和代理模式
两者都是结构型设计模式,相似之处在于,都是真实对象的前面加入一个代理/门面
,但是在意图方面,这两种模式的确存在差异
代理模式 | 门面模式 |
---|---|
为其他对象提供了占位符,以控制原始对象的访问 | 为类的大型子系统提供一个接口 |
具有与其目标对象相同的接口,并保存有对象的引用 | 实现了子系统之间的通信和依赖性的最小化 |
充当客户端和被封装的对象之间的中介 | 提供了单一的简单接口 |