设计模式-结构型模式,代理模式(9)

时间:2022-10-01 07:30:57

在某些应用中,我们想要在访问某个对象之前执行一个或多个重要的操作,例如,访问敏感
信息——在允许用户访问敏感信息之前,我们希望确保用户具备足够的权限。操作系统中也存在
类似的情况,用户必须具有管理员权限才能在系统中安装新程序。
上面提到的重要操作不一定与安全问题相关。延迟初始化
是另一个案例:我们想要把一个计算成本较高的对象的创建过程延迟到用户首次真正使用它时
才进行。

 

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

 

# coding: utf-8


class SensitiveInfo:

    def __init__(self):
        self.users = ['nick', 'tom', 'ben', 'mike']

    def read(self):
        print('There are {} users: {}'.format(len(self.users), ' '.join(self.users)))

    def add(self, user):
        self.users.append(user)
        print('Added user {}'.format(user))


class Info:

    '''SensitiveInfo的保护代理'''

    def __init__(self):
        self.protected = SensitiveInfo()
        self.secret = '0xdeadbeef'

    def read(self):
        self.protected.read()

    def add(self, user):
        sec = input('what is the secret? ')
        self.protected.add(user) if sec == self.secret else print("That's wrong!")


def main():
    info = Info()
    while True:
        print('1. read list |==| 2. add user |==| 3. quit')
        key = input('choose option: ')
        if key == '1':
            info.read()
        elif key == '2':
            name = input('choose username: ')
            info.add(name)
        elif key == '3':
            exit()
        else:
            print('unknown option: {}'.format(key))

if __name__ == '__main__':
    main()

 

代理模式是非常重要常见的一种模式,通过组合一个现有类来改变一些方法。如下,可以通过其他方式定位元素,还能改变元素的输入 点击行为,难点的怎么点击元素都在现有类的方法中实现了,写代理类的方法很简单。

#encoding=utf-8
import time
from selenium import webdriver
from selenium.webdriver.remote.webelement import WebElement


class Browser(object):

    def __init__(self,class_):
        if class_ == webdriver.Chrome:
            self.driver = webdriver.Chrome()


    def find_element_through_id(self,id):
        self.driver.find_element_by_id(id)
        print '通过 ' + id + ' 查找这个元素'




class Element():
    def __init__(self,ele):
        """
        :type ele:WebElement
        """
        self.ele = ele

    def click(self):
        self.ele.click()
        time.sleep(2)

    def input(self,value):
        self.ele.send_keys(value)
        print '填了一个值: ' + value
        time.sleep(3)


browser = Browser(webdriver.Chrome)
Element(browser.find_element_through_id('shurukuang_id')).input('hello world')
Element(browser.find_element_through_id('button_id')).click()

 

 

以上模式,叫代理模式。

2、认真看下,就不难发现,这货其实也可以用继承来解决。?

 

总结下区别就是:

代理在A类中  self.b = B()。self.b.fun() A类的实例通过操作成员变量b来执行方法

继承是 class  A(B)。 self.fun(),A继承B的所有方法

那为啥要代理不要继承?我觉得在灵活性上代理要比继承好,例如上面的selnium例子,Broser可以组合火狐 谷歌 ie多种浏览器,如果继承那就要多种继承了。组合一个什么对象是动态的,继承一个什么类那是固定的是的。

代理模式的类比较干净,不会一股脑复制父类的方法,在智能ide自动补全上好很多,不会一股脑补全父类中在本类中不需用的方法,通过操作成员变量来操作一些方法。

 

知乎的答案就是我要说的意思。

继承和代理都是代码复用的方式。

继承是把要复用的代码(即父对象)的所有属性和行为都复用。不管用得着用不着,先一鼓脑都继承过来。

代理是把要复用的对象作为自己的成员变量,然后在自己的方法中通过这个成员变量去调用要复用的对象的方法。这样就获得了要复用的对象的部分功能,而不用把要复用的对象的全部属性和方法都复用过来。可以称为部分继承或者方法借用模式。相比继承来说,更灵活。



设计模式就是一堆组合和继承的变幻而来。
 
3、除了使用组合对象来实现代理模式和继承来实现代理模式的作用,python是动态语言,属性在运行时确定。
比如不喜欢selneium Chrome类原生的find_element_by_cssselector()方法,想自己叫个简短的方法名,或者修改里面的方法。也可以使用猴子补丁。代码如下
 
 
# coding=utf-8
import time
from selenium.webdriver import Chrome as ChromePatcher
from selenium.webdriver.remote.webelement import WebElement

def by_css(browser, *args):
    print 'hi'  ##举个例子,代表修改原生方法
    return ChromePatcher.find_element_by_css_selector(browser, *args)

ChromePatcher.by_css = by_css      ###赋值,如果函数名与原类同名叫替换方法,不同名叫增加方法。

browser = ChromePatcher()
browser.get('https://www.baidu.com')
browser.by_css('#kw').send_keys(u'美女')

 

      
通过这样就可以修改原类的方法实现或方法名修改。
这种没有用到类对象组合和继承,而是在模块中定义一个方法,然后将原类的方法替换为自己定义的方法,或者增加一个方法。
类外定义方法的时候要记住,第一个参数是代表对象本身,这个不要省略了,否则参数解析出错。第一个参数一般叫self ,也可以叫browser或者任何名称都可以。