Как я могу создать объект, чей производный класс неявно указан в свойствах создания? - PullRequest
3 голосов
/ 16 июня 2009

Я ищу шаблон для следующего. (Я работаю в Perl, но не думаю, что язык имеет особое значение).

с родительским классом Foo и детьми Bar, Baz, Bazza.

Одним из методов построения Foo является синтаксический анализ строки, и часть этой строки будет неявно указывать, какой класс должен быть создан. Так, например, если он запускает «http:», то это Bar, но если он этого не делает, но содержит «[Date]», то Baz это нравится, и так далее.

Теперь, если Foo знает обо всех своих дочерних элементах и ​​о том, какая строка является Bar, что такое Baz и т. Д., Он может вызвать соответствующий конструктор. Но базовый класс не должен иметь никаких знаний о своих детях.

Я хочу, чтобы конструктор Фу мог по очереди испытывать своих детей, пока один из них не скажет: «Да, это мое, я создам вещь».

Я понимаю, что в общем случае эта проблема не является четко определенной, поскольку может быть несколько дочерних элементов, которые примут строку, и поэтому порядок, в котором они называются, имеет значение: игнорируйте это и предполагайте, что характеристики строки таковы, что строка может понравиться только одному дочернему классу.

Лучшее, что я придумал, - это чтобы дочерние классы «регистрировались» в базовом классе при инициализации, чтобы он получал список конструкторов и затем проходил через них. Но есть ли лучший метод, который мне не хватает?

Пример кода:

package Foo;

my @children;

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

sub newFromString
{
  my $string = shift;
  foreach (@children) {
    my $object = $_->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;
}

Ответы [ 4 ]

5 голосов
/ 16 июня 2009

Возможно, вы могли бы реализовать это с помощью Module :: Pluggable ? Это устранит необходимость в регистрации.

Подход, который я использовал ранее, заключался в использовании Module :: Pluggable для загрузки моих дочерних модулей (это позволило мне добавить новые дочерние модули, просто написав и установив их). Каждый из дочерних классов будет иметь конструктор, который либо возвращает благословенный объект, либо undef. Вы перебираете свои плагины, пока не получите объект, а затем возвращаете его.

Что-то вроде:

package MyClass;
use Module::Pluggable;

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

Есть Класс: Фабрика , но это может быть немного выше для ваших нужд.

1 голос
/ 16 июня 2009

Кажется, вы пытаетесь, чтобы один класс был и базовым классом, и фабрикой. Не. Используйте 2 отдельных класса. Примерно так:

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);
}

А затем используйте его как:

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

Таким образом, ваш базовый класс не должен знать о ваших дочерних классах, только ваша фабрика знает. И дочерние классы также не должны знать друг о друге, только Фабрика должна знать детали, какие дочерние классы попробовать и в каком порядке.

0 голосов
/ 16 июня 2009

Если вы берете свой комментарий о том, что родительский класс не содержит информации о детях, и о том, как вы делегировали задачу по установлению соответствия дочерних классов самому классу, тогда, вероятно, будет правильным выделять выбор класса из родительского класса. и создайте синглтон для этой задачи.

по крайней мере, это было бы моим предпочтением ... из-за этого ваш текущий родительский класс (который, вероятно, имеет некоторые общие функции в ваших дочерних классах), вероятно, может затем стать абстрактным или интерфейсом.

Синглтон может затем управлять созданием всех дочерних классов и их распределением (клонировать их, если они не работают?) ... Более того, дочерние классы могут быть перемещены в отдельную dll для обеспечения разделения.

извините, это не прямое решение. Я делал это в прошлом, управляя списком классов в синглтоне так же, как вы здесь. идея синглтона заключается в том, что если вы хотите использовать какое-либо дорогое отражение, вам нужно сделать это только один раз.

0 голосов
/ 16 июня 2009

Вы можете реализовать произвольный алгоритм поиска в классе Foo, который ищет существующие дочерние классы. Может быть, основано на файлах конфигурации, предоставленных дочерними классами, или на любом другом механизме, о котором вы можете подумать.

Затем класс Foo обнаружит существующие клиентские классы во время выполнения и вызовет их по очереди.

Кроме того, вы можете кэшировать результаты поиска и приблизиться к решению реестра, которое вы уже описали сами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...