专业介绍:
为其他对象提供一个代理以控制对这个对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
通俗介绍:
有些情况下,使用者(客户端)不能直接访问某个对象,可能是因为以下几点原因:
1. 需要保护这个对象不被某些客户端/使用者更改。
2. 想要扩展或剪去这个对象的某些功能,但不能/不便于直接修改此对象
3. 直接访问对象的成本太高,需要中间对象代为访问,再提供给使用者
就提供一个中介(代理)给客户端使用,由中介直接访问对象并提供服务给客户端。
举例:研发部要给数据开发部提供一个查询DB数据的接口,数据开发部只需要查询(select)的权限,其他权限都不需要,这个时候,研发部通常会写个基于http协议的接口给它,接口内做好权限的限制,那么这个接口就可以叫做一个数据库访问代理(中间件也可)。
代理模式的角色有3种:
- 抽象主题
- 真实主题
- 代理
代理模式的缺点:
1. 在访问者和被访问者中间增加了一层,降低了一定的访问速度
2. 增加了系统复杂度。
下面的代码解决上面的例子(通过类的形式):
代码:
# 代理模式
import abc, six
# step-1: 定义抽象主题
@six.add_metaclass(abc.ABCMeta)
class AbstractDBAccessSubject:
@abc.abstractmethod
def select(self, sql): pass
@abc.abstractmethod
def delete(self, sql): pass
@abc.abstractmethod
def insert(self, sql): pass
@abc.abstractmethod
def update(self, sql): pass
# step-2: 定义具体主题
class DBAccesSubject(AbstractDBAccessSubject):
def select(self, sql):
return self.execute_sql(sql, 'select')
def delete(self, sql):
return self.execute_sql(sql, 'delete')
def insert(self, sql):
return self.execute_sql(sql, 'insert')
def update(self, sql):
return self.execute_sql(sql, 'update')
def execute_sql(self, sql, op):
return 'real_data', sql, op
# step-3: 定义代理(继承具体主题)
class DBAccesProxy(DBAccesSubject):
def __init__(self, user, passwd):
account_check = {
'user1': {
'passwd': 123, 'permission': ['select']
}
}
self.user = ''
self.permission = []
self.logged = False
u = account_check.get(user, {})
if u.get('passwd') and u.get('passwd') == passwd:
self.user = user
self.permission = u.get('permission')
self.logged = True
return
print('Invalid user and passwd!')
def select(self, sql):
if 'select' not in self.permission:
print('Insufficient authority!')
return
return super(DBAccesProxy, self).select(sql)
def delete(self, sql):
if 'delete' not in self.permission:
print('Insufficient authority!')
return
return super(DBAccesProxy, self).delete(sql)
def insert(self, sql):
if 'insert' not in self.permission:
print('Insufficient authority!')
return
return super(DBAccesProxy, self).insert(sql)
def update(self, sql):
if 'update' not in self.permission:
print('Insufficient authority!')
return
return super(DBAccesProxy, self).update(sql)
if __name__ == '__main__':
db_access_proxy = DBAccesProxy('xxx', 123) # Invalid user and passwd!
# if not db_access_proxy.logged:
# exit()
db_access_proxy = DBAccesProxy('user1', 123)
s = db_access_proxy.select('select * from x')
print(s) # ('real_data', 'select * from x', 'select')
db_access_proxy.update('update x set a=1') # Insufficient authority!
说明:通过代码可以看出,代理类在提供DB访问的基础上,还提供了身份认证和权限控制功能,实现了一种无侵入的扩展。
根据代码还可以看出代理模式的两个缺点:
- 真实主题与代理主题一一对应,增加真实主题也要增加代理。
- 设计代理以前真实主题必须事先存在,不太灵活。(采用动态代理模式可以解决以上问题)
备注:关于abc, six库的基本用法参考文章 简单工厂模式 & Python示例 底部的介绍,: )
欢迎留言~
参考文章: