Как иметь возврат Американского лося экземпляр дочернего класса вместо его собственного класса для полиморфизма

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

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

например: пользователь спрашивает: $file = Repository->new(uri=>'sftp://blabla') .... и возвращается 'Репозиторий:: _Sftp'' экземпляр

Пользователь использовал бы $file как будто это - экземпляр Репозитория без потребности знать реальный подкласс (полиморфизм)

Примечание:
Согласно просьбе возможно, я должен был больше согласиться с тем, чего я пытался достигнуть:
Цель моего класса состоит в том, чтобы смочь добавить новые схемы Repository (например, по sftp) путем простого создания "скрытого" Репозитория:: класс _Stfp и добавление случая в конструкторе Репозитория к фабрике корректный специализированный объект в зависимости от URL. Репозиторий был бы похож на виртуальный базовый класс, обеспечив интерфейс, который специализировался, объекты реализуют.
Все это для добавления новых схем репозитория, не имея остальной части программы, которая будет изменена: это невольно имело бы дело со специализированным экземпляром, как будто это - экземпляр Репозитория.

5
задан Alex F 8 June 2010 в 11:14
поделиться

2 ответа

new строит строитель. Вы хотите, чтобы какой-то другой метод действительно возвращал построенный объект.

Вот пример:

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

Здесь строитель и построенный объект различны. Строитель - это реальный объект, укомплектованный параметрами, которые можно настроить для постройки объекты в зависимости от ситуации. Затем «построенные» объекты просто возвращают значения метода. Это позволяет создавать другие строители в зависимости от ситуации. (Проблема со строителем функции в том, что они очень негибкие - их сложно научить новый частный случай. Эта проблема все еще существует с объектом-строителем, но, по крайней мере, ваше приложение может создать подкласс, создать его экземпляр, и передайте этот объект всему, что нужно для создания объектов. Но В этом случае лучшим подходом является внедрение зависимостей.)

Кроме того, нет необходимости в том, чтобы создаваемые вами репозитории наследовали от что угодно, им просто нужен тег, указывающий, что они репозитории. И это то, что делает наша роль Репозиторий . (Вы захотите добавить Код API здесь и любые методы, которые следует использовать повторно. Но будь осторожен насчет принудительного повторного использования - вы уверены, что все помечено Роль репозитория будет нуждаться в этом коде? Если нет, просто введите код другую роль и примените ее к классам, которые требуют, чтобы функциональность.)

Вот как мы используем созданный нами конструктор. Если, скажем, ты не хочешь прикоснуться к сети:

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

Но если вы это сделаете:

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

Теперь, когда у вас есть строитель, который строит объекты так, как вам нравится, вам просто нужно использовать эти объекты в другом коде. Итак, последний кусок в головоломке имеется в виду "любой" тип объекта Репозитория в другом код. Это просто: вы используете does вместо isa :

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

И все готово.

6
ответ дан 14 December 2019 в 04:30
поделиться

Нет (не напрямую). Обычно в Moose вызов CLASS-> new , где CLASS - это Moose :: Object, вернет экземпляр CLASS.

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

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');
2
ответ дан 14 December 2019 в 04:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: