[pyar] Patrones de diseño: Strategy

Cristhian Boujon cristhianboujon en gmail.com
Mie Feb 21 10:01:10 -03 2018


Hola Luis,

Los patrones de diseño, a priori, no son lenguaje-dependientes, tal es así,
que todos los patrones de diseño pueden explicarse solo con diagramas y
apuntan a solucionar un tipo de problemática común. Dicho ésto, el mismo
patrón de diseño puede ser implementado en distintos lenguajes de
programación, Java, C#, Python, Ruby, PHP, etc. Seguro en cada lenguaje va
a cambiar la implementación y eso está bien que suceda porque hay que
aprovechar las características específicas de cada uno.

Recuerdo en un momento que me surgó la misma duda que vos.
Pasar la clase o función explícitamente en el código es una opción válida,
como sugiere Ivan. Otra opción válida es la de diccionarios, no tiene nada
de malo implementarlos en éste caso, no me queda claro por qué querés
evitarlo.
Supongamos que la decisión de qué leer (la selección de la estrategia)
viene desde el exterior, por ejemplo, del usuario, o sea que se define en
runtime, tu código podría ser algo así:

strategies = {'png': leer_png, 'jpg': leer_jpg, 'pdf': leer_pdf}

lector = strategies[format] # format es una variable que viene definida por
el usuario, por ejemplo, desde la consola.
lector(archivo_cualquiera)

*OOP*
La clase puede no ser necesaria, pero puede, también, ser necesaria si
necesitas almacenar un estado interno, así que una versión OOP podría ser:

class Lector:
def __init__(self, strategy_format):
self.strategy_format = strategy_format

def leer(nombre_archivo):
self.strategy_format(nombre_archivo)

De ésta manera, la clase es totalmente independiente del contexto
específico en el que se la use, no depende de diccionarios o strings, es
decir, la clase espera que se le pase una función strategy_format, sin
importar si se la pasa de manera explícita o si se decide en runtime.
¿Como decidirlo en runtime?
1- Con un diccionario como en primer ejemplo.
strategies = {'png': leer_png, 'jpg': leer_jpg, 'pdf': leer_pdf}
strategy = strategies[format] # format es una variable que viene definida
por el usuario, por ejemplo, desde la consola.

lector = Lector(strategy)
lector.leer(archivo_cualquiera)

2- Con un string usando getattr
def leer_jpg(archivo): # codigo def leer_png(archivo): # codigo

strategy = getattr(self, nombre_function) # Puede ser self o un string con
el path separado por "." en donde se encuentran las funciones
lector = Lector(strategy)
lector.leer(archivo_cualquiera)

Respondiendo tu última pregunta... no, no se puede hacer funcion =
self.'leer_{}'.format(extension) en python, creo que eso funciona en PHP.

Espero que te haya sido útil.
Saludos!


---
*If you want to know more about me, you can check:*
* <http://github.com/Overflow012>
<https://medium.com/@cristhianboujon>*  *[image:
https://www.linkedin.com/in/cristhian-boujon/]
<https://www.linkedin.com/in/cristhian-boujon/> *


2018-02-21 8:19 GMT-03:00 Luis Andraschnik <luis.andraschnik en gmail.com>:

> Hola Ivan
>
> Esoes muy parecido a lo que hice sólo con funciones, ya que lo que quiero
> evitar es a priori saber que función tengo que utilizar sin recurrir a
> if..else, y que el programa "calcule" que función va a usar según el
> contexto.
>
> Saludos!
> Luis
>
> El 20 de febrero de 2018, 19:56, ivan almandoz <alialmandoz en gmail.com>
> escribió:
>
>> hola luis, te paso un snipet que te dejaría hacer lo que necesitas.
>> espero te sirva:
>>
>> class StrategyEjemplo:
>>     def __init__(self, func=None):
>>         if func:
>>             self.execute = func
>>
>>     def execute(self):
>>         print("ningun formato elegido")
>>
>>
>> def leer_png(archivo):
>>     print('leyendo formato {0}'.format(archivo))
>>
>>
>> def leer_jpg(archivo):
>>     print('leyendo formato {0}'.format(archivo))
>>
>>
>> def leer_pdf(archivo):
>>     print('leyendo formato {0}'.format(archivo))
>>
>>
>> if __name__ == "__main__":
>>     strat0 = StrategyEjemplo()
>>     strat1 = StrategyEjemplo(lambda: leer_jpg('jpg'))
>>     strat2 = StrategyEjemplo(lambda: leer_pdf('pdf'))
>>     strat3 = StrategyEjemplo(lambda: leer_png('png'))
>>
>>     strat0.execute()
>>     strat1.execute()
>>     strat2.execute()
>>     strat3.execute()
>>
>>
>>
>> El 20 de febrero de 2018, 16:44, Luis Andraschnik <
>> luis.andraschnik en gmail.com> escribió:
>>
>>> Hola grupo!!
>>>
>>> Leí sobre el patrón de diseño Strategy, más allá de que puede ser
>>> implementado por funciones sin necesidad de recurrir a clases y que los
>>> patrones de diseño son lenguaje-dependientes, veo que en muchos textos
>>> indica que evita el uso de un largo if ..else if ...,
>>>
>>> No obstante no veo que se evite if ...else if ...  , salvo que se
>>> invoquen explícitamente las funciones. Por ejemplo :
>>>
>>> def leer_png(archivo):
>>>
>>>     print(''leí {} png".format(archivo))
>>>
>>> def leer_jpg():
>>>
>>>     print('leí {} jpg".format(archivo))
>>>
>>> def leer_pdf():
>>>
>>>     print('leí {} pdf".format(archivo))
>>>
>>>
>>> def imprimir(file, leer):
>>>
>>>     leer(file)
>>>
>>>
>>> Ejemplo de llamado explícito
>>>
>>> imprimir("archivo.pdf", leer_pdf)
>>>
>>> bárbaro pero muy poco util porque yo no sé de antemano que voy a leer,
>>> entonces redefino imprimir()
>>>
>>>
>>> def imprimir(archivo_cualquiera):
>>>
>>>     if archivo_cualquiera.endswith(".jpg"):
>>>
>>>         leer = leer_jpg
>>>
>>>     else if archivo_cualquiera.endswith(".png"):
>>>
>>>             leer=leer_png
>>>
>>>     else if archivo_cualquiera.endswith(".pdf"):
>>>
>>>             leer=leer_pdf
>>>
>>>     leer(archivo_cualquiera)
>>>
>>>
>>> y entonces dónde es que se evita el if ..else. Y si quiero leer 80
>>> formatos distintos???
>>>
>>>
>>> Sé que puede implementarse un diccionario de funciones, similar a una
>>> sentencia switch, pero no creo que se refieran a eso.
>>>
>>> Los ejemplos que leí siempre hacen un llamado explícito, pero en realida
>>> dlo que interesa es como seleccionar la función que se pasa como parámetro
>>> ...
>>>
>>> Me quedé pensando en esto y no le encuentro la solución o estoy
>>> interpretando algo mal.
>>>
>>> Saluti
>>>
>>> Luis
>>>
>>> _______________________________________________
>>> Lista de Correo de PyAr - Python Argentina - pyar en python.org.ar
>>> Sitio web: http://www.python.org.ar/
>>>
>>> Para administrar la lista (o desuscribirse) entrar a
>>> http://listas.python.org.ar/listinfo/pyar
>>>
>>> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
>>> Argentina - http://www.usla.org.ar
>>>
>>
>>
>> _______________________________________________
>> Lista de Correo de PyAr - Python Argentina - pyar en python.org.ar
>> Sitio web: http://www.python.org.ar/
>>
>> Para administrar la lista (o desuscribirse) entrar a
>> http://listas.python.org.ar/listinfo/pyar
>>
>> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
>> Argentina - http://www.usla.org.ar
>>
>
>
> _______________________________________________
> Lista de Correo de PyAr - Python Argentina - pyar en python.org.ar
> Sitio web: http://www.python.org.ar/
>
> Para administrar la lista (o desuscribirse) entrar a
> http://listas.python.org.ar/listinfo/pyar
>
> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
> Argentina - http://www.usla.org.ar
>
------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: <http://listas.python.org.ar/pipermail/pyar/attachments/20180221/5ae8c011/attachment-0001.html>


Más información sobre la lista de distribución pyar