Come posso creare un oggetto la cui classe derivata è specificata implicitamente dalle proprietà di creazione?

StackOverflow https://stackoverflow.com/questions/1000681

Domanda

Sto cercando un modello per il seguente. (Sto lavorando in Perl, ma non credo che la lingua sia particolarmente importante).

Con una classe genitore Foo e bambini Bar, Baz, Bazza.

Uno dei metodi per costruire un Foo è analizzare una stringa e parte di quella stringa specificherà implicitamente quale classe deve essere creata. Ad esempio, se avvia "http:", allora è una barra, ma se non lo fa ma contiene "[Data]", piace a Baz e così via.

Ora, se Foo conosce tutti i suoi figli, e quale stringa è una barra, cos'è un Baz ecc., può chiamare il costruttore appropriato. Ma una classe base non dovrebbe avere alcuna conoscenza dei suoi figli.

Quello che voglio è che il costruttore di Foo sia in grado di provare i suoi figli a turno, fino a quando uno di loro dice "Sì, questo è mio, creerò la cosa".

Mi rendo conto che nel caso generale questo problema non è ben definito, in quanto potrebbe esserci più di un figlio che accetterà la stringa, e quindi l'ordine in cui vengono chiamati conta: ignora questo e presumi che le caratteristiche della stringa sono tali che a una sola classe figlio piacerà la stringa.

Il meglio che ho escogitato è che le classi secondarie si "registrino" con la classe di base all'inizializzazione, in modo che ottenga un elenco di costruttori e poi li attraversi. Ma c'è un metodo migliore che mi manca?

Codice di esempio:

package Foo;

my @children;

sub _registerChild
{
  push @children, shift();
}

sub newFromString
{
  my $string = shift;
  foreach (@children) {
    my $object = 

Sto cercando un modello per il seguente. (Sto lavorando in Perl, ma non credo che la lingua sia particolarmente importante).

Con una classe genitore Foo e bambini Bar, Baz, Bazza.

Uno dei metodi per costruire un Foo è analizzare una stringa e parte di quella stringa specificherà implicitamente quale classe deve essere creata. Ad esempio, se avvia "http:", allora è una barra, ma se non lo fa ma contiene "[Data]", piace a Baz e così via.

Ora, se Foo conosce tutti i suoi figli, e quale stringa è una barra, cos'è un Baz ecc., può chiamare il costruttore appropriato. Ma una classe base non dovrebbe avere alcuna conoscenza dei suoi figli.

Quello che voglio è che il costruttore di Foo sia in grado di provare i suoi figli a turno, fino a quando uno di loro dice "Sì, questo è mio, creerò la cosa".

Mi rendo conto che nel caso generale questo problema non è ben definito, in quanto potrebbe esserci più di un figlio che accetterà la stringa, e quindi l'ordine in cui vengono chiamati conta: ignora questo e presumi che le caratteristiche della stringa sono tali che a una sola classe figlio piacerà la stringa.

Il meglio che ho escogitato è che le classi secondarie si "registrino" con la classe di base all'inizializzazione, in modo che ottenga un elenco di costruttori e poi li attraversi. Ma c'è un metodo migliore che mi manca?

Codice di esempio:

<*>->newFromString(@_) and return $object; } return undef; } package Bar; our @ISA = ('Foo'); Foo::_registerChild(__PACKAGE__); sub newFromString { my $string = shift; if ($string =~ /^http:/i) { return bless(...); } return undef; }
È stato utile?

Soluzione

Forse potresti implementarlo con Modulo: : Pluggable ? Ciò eliminerebbe la necessità di registrazione.

L'approccio che ho adottato prima era di usare Module :: Pluggable per caricare i miei moduli figlio (questo mi ha permesso di aggiungere nuovi moduli figlio semplicemente scrivendoli e installandoli). Ciascuna delle classi figlio avrebbe un costruttore che restituisce un oggetto benedetto o indefinito. Passa sopra i tuoi plug-in fino a quando non ottieni un oggetto, quindi lo restituisci.

Qualcosa del tipo:

package MyClass;
use Module::Pluggable;

sub new
{
    my ($class, @args) = @_;
    for my $plugin ($class->plugins)
    {
       my $object = $plugin->new(@args);
       return $object if $object;
    }
}

C'è Classe: Factory ma potrebbe essere un po 'esagerato per le tue esigenze.

Altri suggerimenti

Sembra che tu stia cercando di avere una singola classe sia una classe base che una factory. Non farlo. Usa 2 classi separate. Qualcosa del genere:

package Foo;

package Bar;
use base 'Foo';

package Baz;
use base 'Foo';

package Bazza;
use base 'Foo';

package Factory;
use Bar;
use Baz;
use Bazza;

sub get_foo {
    my ($class, $string) = @_;
    return Bar->try($string) || Baz->try($string) || Bazza->try($string);
}

E poi usalo come:

my $foo = Factory->get_foo($string);

In questo modo la tua classe base non ha bisogno di conoscere le tue classi secondarie, solo la tua fabbrica lo fa. E anche le classi per bambini non devono conoscersi, solo Factory deve conoscere i dettagli di quali classi per bambini provare e in quale ordine.

È possibile implementare un algoritmo di ricerca arbitraria nella classe Foo, che cerca le classi figlio esistenti. Forse basato su file di configurazione forniti con classi secondarie o con qualsiasi altro meccanismo a cui potresti pensare.

La classe Foo rileverà quindi le classi client esistenti in fase di esecuzione e le chiamerà a turno.

Inoltre, puoi memorizzare nella cache i risultati della ricerca e avvicinarti alla soluzione di registro che hai già descritto.

Se prendi il tuo commento sulla classe genitore che non contiene informazioni sui bambini e il tuo metodo per delegare il compito di stabilire l'idoneità delle classi figlio alla classe stessa, è probabilmente corretto fattorizzare la selezione della classe dalla classe genitore e crea un singleton per questa attività.

almeno sarebbe la mia preferenza ... da questo la tua attuale classe genitore (che presumibilmente ha alcune funzionalità comuni tra le tue classi figlie) può presumibilmente diventare astratta o un'interfaccia.

il singleton potrebbe quindi gestire la costruzione di tutte le classi figlio e la loro distribuzione (clonarle se non sono funzionali?) ... inoltre le classi figlio possono essere spostate in una DLL separata per promuovere la separazione.

scusa se non è una soluzione diretta. L'ho fatto in passato gestendo un elenco di lezioni nel singleton proprio come se fossi qui. l'idea alla base del singleton è che se vuoi usare qualche riflessione costosa devi farlo una sola volta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top