2009-02-13 4 views
7

Uso el constructor de la clase base como fábrica y cambio de clase en este constructor/fábrica para seleccionar la clase adecuada. ¿Este enfoque es una buena práctica de Python o hay formas más elegantes?¿Utiliza el constructor de la clase base como fábrica en Python?

He intentado leer ayuda sobre metaclases pero sin gran éxito.

Aquí un ejemplo de lo que estoy haciendo.

class Project(object): 
    "Base class and factory." 
    def __init__(self, url): 
     if is_url_local(url): 
     self.__class__ = ProjectLocal 
     else: 
     self.__class__ = ProjectRemote 
     self.url = url 

class ProjectLocal(Project): 
    def do_something(self): 
    # do the stuff locally in the dir pointed by self.url 

class ProjectRemote(Project): 
    def do_something(self): 
    # do the stuff communicating with remote server pointed by self.url 

Tener el código que puede crear la instancia de ProjectLocal/ProjectRemote a través de la clase base del proyecto:

project = Project('http://example.com') 
project.do_something() 

sé que Otra alternativa es el uso de la función de la tela que devolverá el objeto de clase basado en URL , entonces el código se verá similar:

def project_factory(url): 
     if is_url_local(url): 
     return ProjectLocal(url) 
     else: 
     return ProjectRemote(url) 

project = project_factory(url) 
project.do_something() 

¿Mi primer enfoque es solo cuestión de gusto o tiene algunas trampas ocultas?

Respuesta

12

Me quedaría con el enfoque de función de fábrica. Es un python muy estándar y fácil de leer y entender. Puede hacer que sea más genérico manejar más opciones de varias maneras, como pasar la función de discriminación y un mapa de resultados a las clases.

Si el primer ejemplo funciona, es más por suerte que por diseño. ¿Qué sucede si quiere tener un __init__ definido en su subclase?

+5

+1: No intente hacer que la clase base haga nada más que ser la clase base. Las fábricas están siempre separadas. –

+0

Otra buena opción para definir fábricas es classmethods: le dan parametrización de clases de forma gratuita mediante el argumento 'cls'. – andreypopp

0

Creo que el segundo enfoque que utiliza una función de fábrica es mucho más limpio que hacer que la implementación de su clase base dependa de sus subclases.

1

Normalmente tengo una clase de fábrica separada para hacer esto. De esta forma, no es necesario utilizar metacategorías o asignaciones en self.__class__

También trato de evitar poner en conocimiento de la fábrica las clases disponibles para la creación. Más bien, tengo todas las clases disponibles registradas en la fábrica durante la importación del módulo. La clase de dar allí y cierta información sobre cuándo seleccionar esta clase a la fábrica (esto podría ser un nombre, una expresión regular o un invocable (por ejemplo, un método de clase de la clase de registro)).

Funciona muy bien para mí y también implementa cosas como la encapsulación y la ocultación de información.

3

Los siguientes enlaces pueden ser útiles: http://www.suttoncourtenay.org.uk/duncan/accu/pythonpatterns.html#factory http://code.activestate.com/recipes/86900/

Además, como se está utilizando nuevas clases de estilo, utilizando __new__ como la función de fábrica (y no en una clase base, una clase separada es mejor) es lo que generalmente se hace (hasta donde yo sé).

Una función fábrica es generalmente más simple (como otras personas ya han publicado)

Además, no es una buena idea para establecer el atributo __class__ la forma en que lo ha hecho.

Espero que la respuesta y los enlaces sean útiles.

Todo lo mejor.

+0

+1, __new__ es la forma "correcta" de hacerlo, si es que realmente tiene que hacerlo. No recomendado para principiantes sin embargo. Seguir con la función de fábrica para mayor claridad, o refactorizar las clases para que lo local/remoto esté determinado por algo que no sea la clase (por ejemplo, composición). – bobince

+0

Gracias por la bobina de punta. No soy un experto de ninguna manera, así que mis respuestas se basan únicamente en lo que he estudiado en libros. Es genial para obtener algunos consejos prácticos. Gracias de nuevo. – batbrat

+0

Aunque tengo experiencia práctica en Python. Me encanta programar en eso. Sin embargo, no tengo mucho con los patrones de diseño en Python. – batbrat

16

No debe necesitar metaclases para esto.Eche un vistazo al método __new__. Esto le permitirá tomar el control de la creación del objeto, en lugar de solo la inicialización, y así devolver un objeto de su elección.

class Project(object): 
    "Base class and factory." 
    def __new__(cls, url): 
    if is_url_local(url): 
     return super(Project, cls).__new__(ProjectLocal, url) 
    else: 
     return super(Project, cls).__new__(ProjectRemote, url) 

    def __init__(self, url): 
    self.url = url 
+0

Supongo que esto es lo que he buscado pero me pierdo en las metaclases. Gracias. – bialix

+0

funciona, gracias. – bialix

2

Si, como se ha mencionado por @scooterXL, la función de fábrica es el mejor enfoque en ese caso, pero me gusta tener en cuenta un caso de fábricas como classmethods.

Considere la siguiente jerarquía de clases:

class Base(object): 

    def __init__(self, config): 
     """ Initialize Base object with config as dict.""" 
     self.config = config 

    @classmethod 
    def from_file(cls, filename): 
     config = read_and_parse_file_with_config(filename) 
     return cls(filename) 

class ExtendedBase(Base): 

    def behaviour(self): 
     pass # do something specific to ExtendedBase 

Ahora puede crear objetos de base de dict config y del fichero de configuración:

>>> Base({"k": "v"}) 
>>> Base.from_file("/etc/base/base.conf") 

Pero también, puede hacer lo mismo con ExtendedBase gratis :

>>> ExtendedBase({"k": "v"}) 
>>> ExtendedBase.from_file("/etc/extended/extended.conf") 

Por lo tanto, esta fábrica classmethod también se puede considerar como auxiliar constructor.

+0

Me encanta. ¡Gracias! – g33kz0r

Cuestiones relacionadas