跳转至

Python 进阶教程系列 8:代理模式

本文是 Python 进阶教程系列 8,主要介绍了 Python 代理模式。

什么是代理模式?

代理模式是一种结构型设计模式,它允许通过创建一个代理对象来控制对另一个对象的访问。在软件开发中,代理是对象的一种封装,它可以隐藏实际对象的复杂性,并提供简洁的接口供其他对象使用。

代理模式有以下几个优点:

  • 通过使用代理对象,可以对真实对象进行封装和控制,从而隐藏了真实对象的复杂性。
  • 可以通过代理对象实现一些额外的功能,如记录日志、缓存数据、实现懒加载等。
  • 代理模式符合单一职责原则,将对象的职责分离,使得每个对象都可以专注于自己的功能。

代理模式适用于以下场景:

  • 远程访问:当需要从远程服务器访问对象时,可以使用代理模式。代理对象可以处理与远程服务器的通信,并将结果返回给客户端。
  • 虚拟代理:当创建一个非常耗费资源的对象时,可以使用代理模式。代理对象可以在需要时创建和加载真实对象,从而实现延迟加载。
  • 安全代理:当需要对对象的访问进行安全控制时,可以使用代理模式。代理对象可以验证客户端的访问权限,并执行相应的安全检查。

本文部分转载了 【进阶 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 方法。RealSubjectSubject 的具体实现类,它实现了一个真实对象的请求。Proxy 是代理类,它通过持有一个 RealSubject 对象来完成实际请求。

当我们使用代理模式时,可以通过代理对象来调用实际对象的方法。代理对象可以在执行实际请求之前或之后执行额外的操作,例如记录日志、缓存数据或进行安全检查。

让我们看看如何使用这个代理模式:

Python
proxy = Proxy()
proxy.request()

以上代码将输出:

Text Only
代理对象的请求...
真实对象的请求...

在这个例子中,Proxy 对象的 request 方法先打印出代理对象的请求,然后再调用实际对象 RealSubjectrequest 方法。

使用代理模式控制访问权限

场景设定

假如,我们现在想了解一个班级的情况,主要包括两点:人数和学习成绩。

当我们想要了解这个班级的人数时,这个数据不敏感,不涉及隐私,因此可以直接访问。但是,当我们想查询特定某个学生成绩时,这样就涉及隐私信息,需要提供对应学生的姓名 (user_name),访问的密码 (password)。

需求分析

针对这个场景,我们可以先分析一下,访问者就如同上述图中的客户端 (Client),获取班级人数、成绩这些实际的功能是由实体对象实现,也就是图中的 RealSubject。Client 与 RealSubject 之间不能直接通信,它们只能通过中间的代理 (Proxy) 进行通信。而 Proxy 主要的职责就相当于一个控制开关,如果 Client 要访问班级人数,Proxy 会检查这项数据所需要的权限,然后发现班级人数是非敏感数据可以直接访问,那么它会调用实体对象中的方法,返回结果。如果 Client 要访问某个学生的成绩,Proxy 会检查这项数据是敏感数据,需要提供用户名和密码,如果 Client 提供的正确,则允许访问,否则拒绝访问。

编程实践

第一步,我们自定义一个异常处理类,当我们要访问的用户不在班级成绩列表时,则抛出异常。

Python
class NotFindError(Exception):
    def __init__(self, msg):
        self.msg = msg

第二步,实现实体类,实体类实现了具体的功能,针对这个场景就两个方法:获取班级人数、获取指定学生的成绩。

Python
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),它通过对应功能的访问权限来确定是接受这个访问,还是拒绝这个访问。

注意

在这个示例中,我把密码直接写在初始化方法中,实际的项目是不允许这样的,不能把密码写在代码中,另外也不能使用明文密码,需要使用加密工具对明文密码进行加密。

Python
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 个测试用例。

  1. 密码错误,用户名正确;
  2. 密码正确,用户名错误;
  3. 密码正确,用户名正确。

测试用例

密码错误,用户名正确

Text Only
def client():
    proxy = Proxy()
    proxy.get_score("张三")

client()

# shell
You are visiting 张三 score ...
Please input password : kdksla

# 输出
ValueError: The password you provided is wrong!

密码正确,用户名错误

Text Only
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.

密码正确,用户名正确

Text Only
def client():
    proxy = Proxy()
    proxy.get_score("李四")

client()

# shell
You are visiting 张三 score ...
Please input password : 9l0skjlsa

# 输出
The score of 李四 is 59

本讲完整代码如下:

Python
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()

评论