Implementación de News Parser utilizando el patrón de diseño de método de plantilla en Python

Al definir algoritmos, los programadores a menudo descuidan la importancia de agrupar los mismos métodos de diferentes algoritmos. Normalmente, definen algoritmos de principio a fin y repiten los mismos métodos en cada algoritmo. Esta práctica conduce a la duplicación de código y a dificultades en el mantenimiento del código; incluso para un pequeño cambio de lógica, el programador tiene que actualizar el código en varios lugares.   

Un ejemplo común es la creación de autenticación mediante cuentas de redes sociales. El proceso de autenticación que usa diferentes cuentas de redes sociales es similar en general, pero varía ligeramente en el nivel de implementación. Si está definiendo los algoritmos para diferentes cuentas de principio a fin sin separar los métodos comunes, se produce la duplicación del código y las dificultades en el mantenimiento del código. 

El patrón de diseño de plantilla es un patrón de diseño en Python que proporciona un patrón dedicado para evitar la duplicación de código. En este patrón de diseño, los mismos métodos se implementarán en la clase abstracta y los algoritmos que se derivan de esta clase abstracta pueden reutilizar los métodos. Tiene un método de plantilla que facilita la llamada al método para cada algoritmo derivado. Veamos los beneficios del patrón de diseño de plantilla. 

  • Permite que una clase controle y exponga sus partes.
  • Proporciona una gran extensibilidad.
  • Evita la duplicación de código
  • Facilidad de mantenimiento del código

Implementación del analizador de noticias

Implementemos un analizador de noticias para obtener las últimas noticias de diferentes sitios. Aquí, consideramos RSS Feed y Atom Feed para obtener las últimas noticias. Ambos feeds se basan en el protocolo XML, con algunas diferencias en la estructura XML. Puede comprobar la estructura XML de RSS y Atom .

Aquí, nuestro patrón de diseño de plantilla consta de dos clases concretas: YahooNewsParser y GoogleNewsParser , y estas se derivan de una clase abstracta llamada AbstractNewsParser. Esta clase abstracta contiene el método de plantilla, print_latest_news() , que llama a los métodos de operación primitivos. Aquí, los métodos de operación primitivos incluyen tanto algoritmos comunes como algoritmos diferentes, en los que los algoritmos comunes se definen en la propia clase abstracta y los algoritmos diferentes se redefinen en las respectivas clases concretas.

NoticiasParser

Del diagrama anterior, está claro que los métodos de operación primitivos get_url() y parse_content() se redefinen en clases concretas respectivas. Esto se debe a que la estructura de URL y XML difiere de la del feed. Por lo tanto, es necesario redefinir estos métodos para lograr las funcionalidades requeridas. Los otros métodos primitivos como get_raw_content() y content_crop() son métodos comunes y se definen en la propia clase abstracta. El método de plantilla, print_lates_news(), es responsable de llamar a estos métodos primitivos. Entremos en la implementación del código.

Python3

import abc
import urllib.request
from xml.dom.minidom import parseString
  
  
class AbstractNewsParser(object, metaclass=abc.ABCMeta):
    def __init__(self):
        
        # Restrict creating abstract class instance
        if self.__class__ is AbstractNewsParser:
            raise TypeError('Abstract class cannot be instantiated')
  
    def print_latest_news(self):
        """ A Template method, returns 3 latest news for every
    news website """
        url = self.get_url()
        raw_content = self.get_raw_content(url)
        content = self.parse_content(raw_content)
        cropped = self.content_crop(content)
  
        for item in cropped:
            print('Title: ', item['title'])
            print('Content: ', item['content'])
            print('Link: ', item['link'])
            print('Published ', item['published'])
            print('Id: ', item['id'])
  
    @abc.abstractmethod
    def get_url(self):
        pass
  
    def get_raw_content(self, url):
        return urllib.request.urlopen(url).read()
  
    @abc.abstractmethod
    def parse_content(self, content):
        pass
  
    def content_crop(self, parsed_content, max_items=3):
        return parsed_content[:max_items]
  
  
class YahooNewsParser(AbstractNewsParser):
    def get_url(self):
        return 'https://news.yahoo.com/rss/'
  
    def parse_content(self, raw_content):
        yahoo_parsed_content = []
        dom = parseString(raw_content)
  
        for node in dom.getElementsByTagName('item'):
            yahoo_parsed_item = {}
            try:
                yahoo_parsed_item['title'] = node.getElementsByTagName('title')[0].\
                    childNodes[0].nodeValue
            except IndexError:
                yahoo_parsed_item['title'] = None
  
            try:
                yahoo_parsed_item['content'] = node.getElementsByTagName('description')[0].\
                    childNodes[0].nodeValue
            except IndexError:
                yahoo_parsed_item['content'] = None
  
            try:
                yahoo_parsed_item['link'] = node.getElementsByTagName('link')[0].\
                    childNodes[0].nodeValue
            except IndexError:
                yahoo_parsed_item['link'] = None
  
            try:
                yahoo_parsed_item['id'] = node.getElementsByTagName('guid')[0].\
                    childNodes[0].nodeValue
            except IndexError:
                yahoo_parsed_item['id'] = None
  
            try:
                yahoo_parsed_item['published'] = node.getElementsByTagName('pubDate')[0].\
                    childNodes[0].nodeValue
            except IndexError:
                yahoo_parsed_item['published'] = None
  
            yahoo_parsed_content.append(yahoo_parsed_item)
  
        return yahoo_parsed_content
  
  
class GoogleNewsParser(AbstractNewsParser):
    def get_url(self):
        return 'https://news.google.com/atom'
  
    def parse_content(self, raw_content):
        google_parsed_content = []
        dom = parseString(raw_content)
  
        for node in dom.getElementsByTagName('entry'):
            google_parsed_item = {}
  
            try:
                google_parsed_item['title'] = node.getElementsByTagName('title')[0].\
                    childNodes[0].nodeValue
            except IndexError:
                google_parsed_item['title'] = None
  
            try:
                google_parsed_item['content'] = node.getElementsByTagName('content')[0].\
                    childNodes[0].nodeValue
            except IndexError:
                google_parsed_item['content'] = None
  
            try:
                google_parsed_item['link'] = node.getElementsByTagName('href')[0].\
                    childNodes[0].nodeValue
            except IndexError:
                google_parsed_item['link'] = None
  
            try:
                google_parsed_item['id'] = node.getElementsByTagName('id')[0].\
                    childNodes[0].nodeValue
            except IndexError:
                google_parsed_item['id'] = None
  
            try:
                google_parsed_item['published'] = node.getElementsByTagName('title')[0].\
                    childNodes[0].nodeValue
            except IndexError:
                google_parsed_item['published'] = None
  
            google_parsed_content.append(google_parsed_item)
  
        return google_parsed_content
  
  
class NewsParser(object):
    def get_latest_news(self):
        yahoo = YahooNewsParser()
        print('Yahoo: \n', yahoo.print_latest_news())
        print()
        print()
        google = GoogleNewsParser()
        print('Google: \n', google.print_latest_news())
  
  
if __name__ == '__main__':
    newsParser = NewsParser()
    newsParser.get_latest_news()

Producción

Analizador de noticias de Yahoo

Analizador de noticias de Google

Un patrón de diseño de plantilla proporciona la mejor solución de diseño cuando tiene un algoritmo que tiene el mismo comportamiento con un proceso de implementación diferente. Ayuda a diseñar una estructura estándar para un algoritmo de tal manera que las clases derivadas puedan redefinir los pasos sin cambiar la estructura.  

Publicación traducida automáticamente

Artículo escrito por SonuGeorge y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *