Как я могу создать объект, чей производный класс указан неявно свойствами создания?

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

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

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

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

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

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

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

Образец кода:

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

perl,inheritance,constructor,design-patterns,creation,

3

Ответов: 4


5 принят

Возможно, вы могли бы реализовать это с помощью модуля :: 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

Кажется, вы пытаетесь создать один класс как базового класса, так и фабрики. Не. Используйте 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);

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


0

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

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

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


0

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

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

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

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

Perl, наследование, конструктор, дизайн-шаблоны, создание,
Похожие вопросы