📜  在Python中使用工厂方法设计模式访问 Web 资源

📅  最后修改于: 2022-05-13 01:55:16.346000             🧑  作者: Mango

在Python中使用工厂方法设计模式访问 Web 资源

工厂是用于构建其他对象的类,它根据传递的参数创建并返回一个对象。在这里,客户端向 Factory 类提供材料,Factory 类根据给定的材料创建并返回产品。工厂不是设计模式,但它是工厂方法设计模式的基础。

在进入工厂方法设计模式之前,让我们创建一个简单工厂。在下面的示例中, SimpleFactory类有一个名为build_connection()的静态方法,用于根据接收到的参数创建对象。

Python3
import http.client
from ftplib import FTP
  
  
class SimpleFactory(object):
    
    @staticmethod
    def build_connection(protocol):
        if protocol == 'https':
            return http.client.HTTPSConnection('www.python.org')
        elif protocol == 'ftp':
            return FTP('ftp1.at.proftpd.org')
        else:
            raise RuntimeError('Unknown protocol')
  
  
if __name__ == '__main__':
    
    input_protocol = input('Which protocol to use? (https or ftp):')
    protocol = SimpleFactory.build_connection(input_protocol)
      
    if input_protocol == 'https':
        protocol.request("GET", "/")
        resp = protocol.getresponse()
        print(resp.status, resp.reason)
      
    elif input_protocol == 'ftp':
        resp = protocol.login()
        print(resp)


Python3
import abc
import urllib
import urllib.error
import urllib.request
from bs4 import BeautifulSoup
  
  
class Connector(metaclass=abc.ABCMeta):
    def __init__(self, is_secure):
        self.is_secure = is_secure
        self.port = self.factory_port()
        self.protocol = self.factory_protocol()
  
    @abc.abstractmethod
    def crawl(self):
        pass
  
    def scan(self, con_domain, con_path):
        url = self.protocol + '://'+ con_domain \
              + ':' + str(self.port) + con_path
        print(url)
        return urllib.request.urlopen(url, timeout=10).read()
  
    @abc.abstractmethod
    def factory_protocol(self):
        pass
  
    @abc.abstractmethod
    def factory_port(self):
        pass
  
class HTTPConnector(Connector):
    """ Creates an HTTP Connector """
  
    def factory_protocol(self):
        if self.is_secure:
            return 'https'
        return 'http'
  
    def factory_port(self):
        if self.is_secure:
            return '443'
        return '80'
  
    def crawl(self, data):
        """ crawls web content """
        filenames = []
        soup = BeautifulSoup(data,"html.parser")
        links = soup.table.findAll('a')
          
        for link in links:
            filenames.append(link['href'])
        return '\n'.join(filenames)
  
class FTPConnector(Connector):
    def factory_protocol(self):
        return 'ftp'
  
    def factory_port(self):
        return '21'
  
    def crawl(self, data):
        # converting byte to string
        data = str(data, 'utf-8')
        lines = data.split('\n')
        filenames = []
        for line in lines:
            extract_line = line.split(None, 8)
            if len(extract_line) == 9:
                filenames.append(extract_line[-1])
                  
        return '\n'.join(filenames)
  
if __name__ == "__main__":
    con_domain = 'ftp.freebsd.org'
    con_path = '/pub/FreeBSD/'
  
    con_protocol = input('Choose the protocol \
                    (0-http, 1-ftp): ')
      
    if con_protocol == '0':
        is_secure = input('Use secure connection? (1-yes, 0-no):')
        if is_secure == '1':
            is_secure = True
        else:
            is_secure = False
        connector = HTTPConnector(is_secure)
    else:
        is_secure = False
        connector = FTPConnector(is_secure)
  
    try:
        data = connector.scan(con_domain, con_path)
    except urllib.error.URLError as e:
        print('Cannot access resource with this method', e)
    else:
        print(connector.crawl(data))


让我们看看输出

输出

在这里,您提供协议类型作为参数,工厂类根据该参数创建并返回对象。需要注意的一点是,对象的创建不是客户端的责任。

工厂方法设计模式

工厂方法设计模式与简单工厂相同,但与简单工厂结构相比,其结构复杂。它有一个包含工厂方法(用于创建对象)和操作方法(用于处理创建的对象)的抽象类。并且,创建对象的具体类是从抽象类派生的。

这种模式有助于定义一个接口来创建一个对象。这里实现接口的类决定实例化哪个类。因此,抽象类中的操作方法在实现产品接口之前不必担心对象的创建。

注意:如果您是初学者,我强烈建议您阅读工厂方法 – Python设计模式。

工厂方法设计模式的优点

让我们来看看工厂设计模式的优点。其中一些是:

  1. 它使代码更通用
  2. 它将接口与实现分开
  3. 它降低了代码维护的复杂性。

使用不同的协议访问 Web 资源

让我们实现一个使用 HTTP 或 FTP 协议访问 Web 资源的程序。在这里,我们将使用允许 HTTP 和 FTP 协议的站点 ftp.freebsd.org。

工厂方法设计模式

从上图可以看出,设计中有一个抽象类叫Connector,还有两个具体的类——HTTPConnector和FTPConnector。这两个具体类派生自Connector类,抽象类中的工厂方法使用这些类进行产品创建。让我们看看下面的代码。

蟒蛇3

import abc
import urllib
import urllib.error
import urllib.request
from bs4 import BeautifulSoup
  
  
class Connector(metaclass=abc.ABCMeta):
    def __init__(self, is_secure):
        self.is_secure = is_secure
        self.port = self.factory_port()
        self.protocol = self.factory_protocol()
  
    @abc.abstractmethod
    def crawl(self):
        pass
  
    def scan(self, con_domain, con_path):
        url = self.protocol + '://'+ con_domain \
              + ':' + str(self.port) + con_path
        print(url)
        return urllib.request.urlopen(url, timeout=10).read()
  
    @abc.abstractmethod
    def factory_protocol(self):
        pass
  
    @abc.abstractmethod
    def factory_port(self):
        pass
  
class HTTPConnector(Connector):
    """ Creates an HTTP Connector """
  
    def factory_protocol(self):
        if self.is_secure:
            return 'https'
        return 'http'
  
    def factory_port(self):
        if self.is_secure:
            return '443'
        return '80'
  
    def crawl(self, data):
        """ crawls web content """
        filenames = []
        soup = BeautifulSoup(data,"html.parser")
        links = soup.table.findAll('a')
          
        for link in links:
            filenames.append(link['href'])
        return '\n'.join(filenames)
  
class FTPConnector(Connector):
    def factory_protocol(self):
        return 'ftp'
  
    def factory_port(self):
        return '21'
  
    def crawl(self, data):
        # converting byte to string
        data = str(data, 'utf-8')
        lines = data.split('\n')
        filenames = []
        for line in lines:
            extract_line = line.split(None, 8)
            if len(extract_line) == 9:
                filenames.append(extract_line[-1])
                  
        return '\n'.join(filenames)
  
if __name__ == "__main__":
    con_domain = 'ftp.freebsd.org'
    con_path = '/pub/FreeBSD/'
  
    con_protocol = input('Choose the protocol \
                    (0-http, 1-ftp): ')
      
    if con_protocol == '0':
        is_secure = input('Use secure connection? (1-yes, 0-no):')
        if is_secure == '1':
            is_secure = True
        else:
            is_secure = False
        connector = HTTPConnector(is_secure)
    else:
        is_secure = False
        connector = FTPConnector(is_secure)
  
    try:
        data = connector.scan(con_domain, con_path)
    except urllib.error.URLError as e:
        print('Cannot access resource with this method', e)
    else:
        print(connector.crawl(data))
         

在这个程序中,我们使用 HTTP 或 FTP 协议连接到一个网页。 Connector抽象类旨在建立连接( scan方法),以及用于抓取页面( crawl方法)。除此之外,它还提供了两个工厂方法—— factory_protocolfactory_port——来处理协议和端口地址。

让我们看看输出。

输出

概括

工厂类用于创建其他类,它使代码更具通用性。工厂类中的工厂方法充当创建对象的接口,从而将接口与实现分开。因此,类可以实现这些接口方法并可以决定实例化哪个类。