Python 进阶教程系列 8:代理模式¶
本文是 Python 进阶教程系列 8,主要介绍了 Python 代理模式。
什么是代理模式?¶
代理模式是一种结构型设计模式,它允许通过创建一个代理对象来控制对另一个对象的访问。在软件开发中,代理是对象的一种封装,它可以隐藏实际对象的复杂性,并提供简洁的接口供其他对象使用。
代理模式有以下几个优点:
- 通过使用代理对象,可以对真实对象进行封装和控制,从而隐藏了真实对象的复杂性。
- 可以通过代理对象实现一些额外的功能,如记录日志、缓存数据、实现懒加载等。
- 代理模式符合单一职责原则,将对象的职责分离,使得每个对象都可以专注于自己的功能。
代理模式适用于以下场景:
- 远程访问:当需要从远程服务器访问对象时,可以使用代理模式。代理对象可以处理与远程服务器的通信,并将结果返回给客户端。
- 虚拟代理:当创建一个非常耗费资源的对象时,可以使用代理模式。代理对象可以在需要时创建和加载真实对象,从而实现延迟加载。
- 安全代理:当需要对对象的访问进行安全控制时,可以使用代理模式。代理对象可以验证客户端的访问权限,并执行相应的安全检查。
本文部分转载了 【进阶 Python】第八讲:代理模式。
代理模式的实现示例¶
在 Python 中,我们可以使用类来实现代理模式。以下是一个简单的示例:
class Subject:
def request(self):
pass
class RealSubject(Subject):
def request(self):
print("真实对象的请求...")
class Proxy(Subject):
def __init__(self):
self.real_subject = RealSubject()
def request(self):
# 可以在代理对象中执行一些附加操作
print("代理对象的请求...")
self.real_subject.request()
在上面的示例中,Subject
是一个抽象类,定义了一个 request
方法。RealSubject
是 Subject
的具体实现类,它实现了一个真实对象的请求。Proxy
是代理类,它通过持有一个 RealSubject
对象来完成实际请求。
当我们使用代理模式时,可以通过代理对象来调用实际对象的方法。代理对象可以在执行实际请求之前或之后执行额外的操作,例如记录日志、缓存数据或进行安全检查。
让我们看看如何使用这个代理模式:
以上代码将输出:
在这个例子中,Proxy
对象的 request
方法先打印出代理对象的请求,然后再调用实际对象 RealSubject
的 request
方法。
使用代理模式控制访问权限¶
场景设定¶
假如,我们现在想了解一个班级的情况,主要包括两点:人数和学习成绩。
当我们想要了解这个班级的人数时,这个数据不敏感,不涉及隐私,因此可以直接访问。但是,当我们想查询特定某个学生成绩时,这样就涉及隐私信息,需要提供对应学生的姓名 (user_name),访问的密码 (password)。
需求分析¶
针对这个场景,我们可以先分析一下,访问者就如同上述图中的客户端 (Client),获取班级人数、成绩这些实际的功能是由实体对象实现,也就是图中的 RealSubject。Client 与 RealSubject 之间不能直接通信,它们只能通过中间的代理 (Proxy) 进行通信。而 Proxy 主要的职责就相当于一个控制开关,如果 Client 要访问班级人数,Proxy 会检查这项数据所需要的权限,然后发现班级人数是非敏感数据可以直接访问,那么它会调用实体对象中的方法,返回结果。如果 Client 要访问某个学生的成绩,Proxy 会检查这项数据是敏感数据,需要提供用户名和密码,如果 Client 提供的正确,则允许访问,否则拒绝访问。
编程实践¶
第一步,我们自定义一个异常处理类,当我们要访问的用户不在班级成绩列表时,则抛出异常。
第二步,实现实体类,实体类实现了具体的功能,针对这个场景就两个方法:获取班级人数、获取指定学生的成绩。
class RealSubject(object):
def __init__(self):
self.score = {"张三": 90, "李四": 59, "王二": 61}
def num_students(self):
num = len(self.score.keys())
print("The number of students is {num}".format(num=num))
def get_score(self, user_name):
_score = self.score.get(user_name)
print("The score of {user} is {score}".format(user=user_name, score=_score))
第三步,实现代理 (Proxy),它通过对应功能的访问权限来确定是接受这个访问,还是拒绝这个访问。
注意
在这个示例中,我把密码直接写在初始化方法中,实际的项目是不允许这样的,不能把密码写在代码中,另外也不能使用明文密码,需要使用加密工具对明文密码进行加密。
class Proxy(object):
def __init__(self):
self.default_passwd = "9l0skjlsa"
self.real_subject = RealSubject()
def num_students(self):
self.real_subject.num_students()
def get_score(self, user_name):
print("You are visiting {} score ...".format(user_name))
passwd = input("Please input password : ")
if passwd == self.default_passwd:
if user_name in self.real_subject.score.keys():
return self.real_subject.get_score(user_name)
else:
raise NotFindError("The student you are visiting not found.")
else:
raise ValueError("The password you provided is wrong!")
然后就是实现 Client 来调用对应的功能,为了测试上述代理的功能,使用 3 个测试用例。
- 密码错误,用户名正确;
- 密码正确,用户名错误;
- 密码正确,用户名正确。
测试用例¶
密码错误,用户名正确¶
def client():
proxy = Proxy()
proxy.get_score("张三")
client()
# shell
You are visiting 张三 score ...
Please input password : kdksla
# 输出
ValueError: The password you provided is wrong!
密码正确,用户名错误¶
def client():
proxy = Proxy()
proxy.get_score("李三")
client()
# shell
You are visiting 张三 score ...
Please input password : 9l0skjlsa
# 输出
NotFindError: The student you are visiting not found.
密码正确,用户名正确¶
def client():
proxy = Proxy()
proxy.get_score("李四")
client()
# shell
You are visiting 张三 score ...
Please input password : 9l0skjlsa
# 输出
The score of 李四 is 59
本讲完整代码如下:
class NotFindError(Exception):
def __init__(self, msg):
self.msg = msg
class RealSubject(object):
def __init__(self):
self.score = {"张三": 90, "李四": 59, "王二": 61}
def num_students(self):
num = len(self.score.keys())
print("The number of students is {num}".format(num=num))
def get_score(self, user_name):
_score = self.score.get(user_name)
print("The score of {user} is {score}".format(user=user_name, score=_score))
class Proxy(object):
def __init__(self):
self.default_passwd = "9l0skjlsa"
self.real_subject = RealSubject()
def num_students(self):
self.real_subject.num_students()
def get_score(self, user_name):
print("You are visiting {} score ...".format(user_name))
passwd = input("Please input password : ")
if passwd == self.default_passwd:
if user_name in self.real_subject.score.keys():
return self.real_subject.get_score(user_name)
else:
raise NotFindError("The student you are visiting not found.")
else:
raise ValueError("The password you provided is wrong!")
def client():
proxy = Proxy()
proxy.get_score("张三")
client()
Python 进阶教程系列文章