2010-06-08 9 views
5

Quiero crear una clase genérica, cuyo constructor no devolvería una instancia de esta clase genérica, sino una instancia de una clase secundaria dedicada.Cómo hacer que Moose devuelva una instancia de clase infantil en lugar de su propia clase, para polimorfismo

Como Moose crea objetos automáticamente, no entiendo si esto es posible y cómo crear una clase Moose con la sintaxis de Moose y tener este comportamiento.

por ejemplo: el usuario pide: $file = Repository->new(uri=>'sftp://blabla') .... y se devuelve un repositorio `:: _ ejemplo Sftp``

usuario utilizaría $file como si se trata de una instancia del repositorio, sin la necesidad de conocer la verdadera subclase (polimorfismo)

Nota:
a lo solicitado, tal vez debería haber sido más claro acerca de lo que estaba tratando de lograr:
El propósito de mi clase es ser capaz de añadir nuevos esquemas de repositorio (por ejemplo, durante sftp), simplemente creando una clase Repository :: _ Stfp "oculta" y agregando un caso en el constructor Repository para fabricar el objeto especializado correcto dependiendo de url. El repositorio sería como una clase base virtual, proporcionando una interfaz que los objetos especializados implementarían.
Todo esto es para agregar nuevos esquemas de repositorio sin tener que modificar el resto del programa: trataría sin saberlo la instancia especializada como si fuera una instancia de Repositorio.

Respuesta

6

new construye el constructor. Desea algún otro método para devolver realmente el objeto construido.

He aquí un ejemplo:

class RepositoryBuilder { 
    has 'allow_network_repositories' => (
     is  => 'ro', 
     isa  => 'Bool', 
     required => 1, 
    ); 

    method build_repository(Uri $url) { 
     confess 'network access is not allowed' 
      if $url->is_network_url && !$self->allow_network_repositories; 

     my $class = $self->determine_class_for($url); # Repository::Whatever 
     return $class->new(url => $url); 
    } 
    } 

    role Repository { <whatever } 

    class Repository::File with Repository {} 
    class Repository::HTTP with Repository {} 

Aquí, el constructor y el objeto construido son distintos. El constructor es un objeto real , con parámetros que se pueden personalizar para crear objetos según lo requiera la situación. Entonces, los objetos "construidos" son simplemente devuelven valores de un método. Esto le permite construir otros constructores dependiendo de la situación. (Un problema con las funciones del generador es que son muy inflexibles: es difícil enseñarles un nuevo caso especial. Este problema todavía existe con un objeto generador, pero al menos su aplicación puede crear una subclase, instanciarlo, y pasar este objeto para todo lo que necesita para crear objetos. Pero inyección de dependencia es un mejor enfoque en este caso.)

Además, no hay necesidad de que los repositorios que construir a heredar de nada, sólo necesitan una etiqueta que indica que son repositorios. Y eso es lo que hace nuestra función Repository. (Deberá agregar el código API aquí y cualquier método que deba reutilizarse.Pero tenga cuidado sobre forzar la reutilización. ¿Está seguro de que todo lo que esté etiquetado con la función de repositorio querrá ese código? Si no es así, sólo hay que poner el código en otro papel y aplicar que una a las clases que requieren que funcionalidad.)

Así es como se utiliza el constructor creamos. Si, por ejemplo, no quieren toque la red:

my $b = RepositoryBuilder->new(allow_network_repositories => 0); 
$b->build_repository('http://google.com/'); # error 
$b->build_repository('file:///home/whatever'); # returns a Repository::Foo 

Pero si lo hace:

my $b = RepositoryBuilder->new(allow_network_repositories => 1); 
$b->build_repository('http://google.com/'); # Repository::HTTP 

Ahora que tiene un constructor que construye los objetos a su gusto, solo necesita usar estos objetos en otro código. Así que la última pieza en el rompecabezas se refiere a "cualquier" tipo de objeto de repositorio en otro código . Eso es simple, se utiliza en lugar de doesisa:

class SomethingThatHasARepository { 
    has 'repository' => (
     is  => 'ro', 
     does  => 'Repository', 
     required => 1, 
    ); 
} 

Y ya está.

+0

mmh ... déjenme entender esta función, volviendo a los documentos de Moose ... También, mi primer pensamiento al respecto, ya que acabo de comenzar con Moose , es si todavía es mejor hacer Moose malo que no Moose –

+0

@alex: si sigues los ejemplos en Moose :: Manual :: *, puedes usar la mayoría de las características de Moose sin tener problemas. Es solo cuando comienzas a usar 'meta' que las cosas se vuelven locas :) – Ether

+0

bien, entendido, pero tengo curiosidad acerca de las diferencias sutiles: a) Un 'Repositorio' de clase base Moose con todos los atributos básicos; Repositorio de clases especializadas de Moose :: _ Sftp 'que "extiende"/hereda la clase base; Un alce (o no) fábrica 'RepositoryBuilder', con un método para crearme una instancia de la clase especializada, que puede ser agnósticamente manipulado como un 'Repositorio'. b) Lo mismo que a), pero con un rol en lugar de la clase base <- que es lo que usted propuso, ¿verdad? c) yd) MooseX :: AbstractFactory o MooseX :: ABC, pero estos no están disponibles por defecto (tomo centos5 para ref) –

2

No (no directamente). En general, en Moose, llamando al CLASS->new donde CLASS isa Moose :: Object devolverá una instancia de CLASS.

¿Puede describir con más detalle lo que está tratando de lograr y por qué cree que es esto lo que quiere? Probablemente desee construir una clase de fábrica - cuando llame a un método en ella, llamará al constructor de la clase apropiada y le devolverá ese objeto, sin que tenga que preocuparse por el tipo particular que obtiene:

package MyApp::Factory::Repository; 

sub getFactory 
{ 
    my ($class, %attrs); 

    # figure out what the caller wants, and decide what type to return 
    $class ||= 'Repository::_Sftp'; 
    return $class->new(attr1 => 'foo', attr2 => 'bar', %attrs); 
} 

my $file = MyApp::Factory::Repository->getFactory(uri=>'sftp://blabla'); 
+0

Esto es algo de lo que tenía antes, que también estaba usando con Class :: Accessor. Y alguien en una pregunta que le pregunté ayer sobre C: A mencionó que debería probar Moose (respuesta no relacionada con este caso). Así que es por eso que estaba tratando de "moosificar" mi tipo de fábrica. En cuanto al propósito, agregué una nota a la pregunta principal –

Cuestiones relacionadas