在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设计模式。
工厂方法设计模式的优点
让我们来看看工厂设计模式的优点。其中一些是:
- 它使代码更通用
- 它将接口与实现分开
- 它降低了代码维护的复杂性。
使用不同的协议访问 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_protocol和factory_port——来处理协议和端口地址。
让我们看看输出。
概括
工厂类用于创建其他类,它使代码更具通用性。工厂类中的工厂方法充当创建对象的接口,从而将接口与实现分开。因此,类可以实现这些接口方法并可以决定实例化哪个类。