Im Entwurfsmuster von Abstract Factory verfügt jedes Produkt über eine abstrakte Produktschnittstelle. Dieser Ansatz erleichtert die Erstellung von Familien verwandter Objekte, die unabhängig von ihren Fabrikklassen sind. Infolgedessen können Sie die Factory zur Laufzeit ändern, um ein anderes Objekt zu erhalten. Dies vereinfacht das Ersetzen der Produktfamilien.   

In diesem Entwurfsmuster verwendet der Client eine abstrakte Factory-Schnittstelle, um auf Objekte zuzugreifen. Die abstrakte Schnittstelle trennt die Erstellung von Objekten vom Client, was die Manipulation erleichtert und die konkreten Klassen vom Client isoliert. Das Hinzufügen neuer Produkte zur vorhandenen Factory ist jedoch schwierig, da Sie die Factory-Schnittstelle erweitern müssen, einschließlich des Änderns der abstrakten Factory-Schnittstellenklasse und aller ihrer Unterklassen.  

Schauen wir uns zum besseren Verständnis die Implementierung des Webcrawlers in Python an. Wie im folgenden Diagramm gezeigt, haben Sie eine abstrakte Factory-Schnittstellenklasse - AbstractFactory - und zwei konkrete Factory-Klassen - HTTPConcreteFactory und FTPConcreteFactory . Diese beiden konkreten Klassen sind von der AbstractFactory-Klasse abgeleitet und verfügen über Methoden zum Erstellen von Instanzen von drei Schnittstellen - ProtocolAbstractProduct , PortAbstractProduct und CrawlerAbstractProduct .

Da die AbstractFactory- Klasse als Schnittstelle für Fabriken wie HTTPConcreteFactory und FTPConcreteFactory fungiert , verfügt sie über drei abstrakte Methoden: create_protocol(), create_port(), create_crawler() . Diese Methoden werden in den Factory-Klassen neu definiert. Dies bedeutet, dass die HTTPConcreteFactory- Klasse ihre Familie verwandter Objekte wie HTTPPort, HTTPSecurePort und HTTPSecureProtocol erstellt, während die FTPConcreteFactory- Klasse FTPPort, FTPProtocol und FTPCrawler erstellt.



import abc 
import urllib 
import urllib.error 
import urllib.request 
from bs4 import BeautifulSoup 
  
class AbstractFactory(object, metaclass=abc.ABCMeta): 
    
      
    def __init__(self, is_secure): 
        self.is_secure = is_secure 
  
    @abc.abstractmethod 
    def create_protocol(self): 
        pass
  
    @abc.abstractmethod 
    def create_port(self): 
        pass
  
    @abc.abstractmethod 
    def create_crawler(self): 
        pass
  
class HTTPConcreteFactory(AbstractFactory): 
    
      
    def create_protocol(self): 
        if self.is_secure: 
            return HTTPSecureProtocol() 
        return HTTPProtocol() 
  
    def create_port(self): 
        if self.is_secure: 
            return HTTPSecurePort() 
        return HTTPPort() 
  
    def create_crawler(self): 
        return HTTPCrawler() 
  
class FTPConcreteFactory(AbstractFactory): 
    
      
    def create_protocol(self): 
        return FTPProtocol() 
  
    def create_port(self): 
        return FTPPort() 
  
    def create_crawler(self): 
        return FTPCrawler() 
  
class ProtocolAbstractProduct(object, metaclass=abc.ABCMeta): 
    
      
    @abc.abstractmethod 
    def __str__(self): 
        pass
      
class HTTPProtocol(ProtocolAbstractProduct): 
    
      
    def __str__(self): 
        return 'http'
  
class HTTPSecureProtocol(ProtocolAbstractProduct): 
    
      
    def __str__(self): 
        return 'https'
  
class FTPProtocol(ProtocolAbstractProduct): 
    
      
    def __str__(self): 
        return 'ftp'
  
class PortAbstractProduct(object, metaclass=abc.ABCMeta): 
    
      
    @abc.abstractmethod 
    def __str__(self): 
        pass
  
class HTTPPort(PortAbstractProduct): 
    
      
    def __str__(self): 
        return '80'
  
class HTTPSecurePort(PortAbstractProduct): 
    
    def __str__(self): 
        return '443'
  
class FTPPort(PortAbstractProduct): 
    
      
    def __str__(self): 
        return '21'
  
class CrawlerAbstractProduct(object, metaclass=abc.ABCMeta): 
    
      
    @abc.abstractmethod 
    def __call__(self, content): 
        pass
  
class HTTPCrawler(CrawlerAbstractProduct): 
    def __call__(self, content): 
        
          
        filenames = [] 
        soup = BeautifulSoup(content, "html.parser") 
        links = soup.table.findAll('a') 
  
        for link in links: 
            filenames.append(link['href']) 
              
        return '\n'.join(filenames) 
  
class FTPCrawler(CrawlerAbstractProduct): 
    def __call__(self, content): 
        
        
        content = str(content, 'utf-8') 
        lines = content.split('\n') 
        filenames = [] 
          
        for line in lines: 
            splitted_line = line.split(None, 8) 
            if len(splitted_line) == 9: 
                filenames.append(splitted_line[-1]) 
  
        return '\n'.join(filenames) 
  
class Connector(object): 
    
      
    def __init__(self, abstractfactory): 
        
          
        self.protocol = abstractfactory.create_protocol() 
        self.port = abstractfactory.create_port() 
        self.crawl = abstractfactory.create_crawler() 
  
    def read(self, host, path): 
        url = str(self.protocol) + '://' + host + ':' + str(self.port) + path 
        print('Connecting to', url) 
        return urllib.request.urlopen(url, timeout=10).read() 
  
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
        abstractfactory = HTTPConcreteFactory(is_secure) 
    else: 
        is_secure = False
        abstractfactory = FTPConcreteFactory(is_secure) 
  
    connector = Connector(abstractfactory) 
  
    try: 
        data = connector.read(con_domain, con_path) 
    except urllib.error.URLError as e: 
        print('Cannot access resource with this method', e) 
    else: 
        print(connector.crawl(data)) 

Ausgabe

Ausgabe

Ziel des Programms ist das Crawlen der Website mithilfe des HTTP- oder FTP-Protokolls. Hier müssen wir drei Szenarien bei der Implementierung des Codes berücksichtigen. 

  1. Protokoll
  2. Hafen
  3. Crawler

Diese drei Szenarien unterscheiden sich in den HTTP- und FTP-Webzugriffsmodellen. Hier müssen wir also zwei Fabriken erstellen, eine zum Erstellen von HTTP-Produkten und eine zum Erstellen von FTP-Produkten - HTTPConcreteFactory und FTPConcreteFactory . Diese beiden konkreten Fabriken stammen aus einer abstrakten Fabrik - AbstractFactory

Eine abstrakte Schnittstelle wird verwendet, da die Operationsmethoden für beide Factory-Klassen gleich sind, nur die Implementierung unterschiedlich ist und daher der Client-Code bestimmen kann, welche Factory zur Laufzeit verwendet werden soll. Lassen Sie uns die von jeder Fabrik erstellten Produkte analysieren. 

Im Fall eines Protokollprodukts erstellt die HTTP-Betonfabrik entweder das http- oder das https-Protokoll, während die FTP-Betonfabrik das FTP-Protokoll erstellt. Für Hafenprodukte generiert die HTTP-Betonfabrik entweder 80 oder 443 als Hafenprodukt, und die FTP-Fabrik generiert 21 als Hafenprodukt. Und schließlich unterscheidet sich die Crawler-Implementierung, da die Website-Struktur für HTTP und FTP unterschiedlich ist.  

Hier hat das erstellte Objekt dieselbe Schnittstelle, während die erstellten konkreten Objekte für jede Fabrik unterschiedlich sind. Angenommen, die Portprodukte wie HTTP-Port, HTTP Secure Port und FTP-Port haben dieselbe Schnittstelle, aber die konkreten Objekte für beide Fabriken sind unterschiedlich. Gleiches gilt auch für Protokoll und Crawler. 

Schließlich akzeptiert die Connector-Klasse eine Factory und verwendet diese Factory, um alle Attribute des Connectors basierend auf der Factory-Klasse einzufügen.