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

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

Вопрос

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

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

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

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

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

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

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

Пример кода:

package Foo;

my @children;

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

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

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

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

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

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

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

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

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

Пример кода:

<*>->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; }
Это было полезно?

Решение

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

Подход, который я использовал ранее, заключался в использовании 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;
    }
}

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

Другие советы

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top