对于多态,如何让Moose返回子类实例而不是它自己的类

时间:2010-06-08 00:25:28

标签: perl polymorphism moose

我想创建一个泛型类,其构建器不会返回此泛型类的实例,而是返回专用子类的实例。

当Moose进行自动对象构建时,我无法理解这是否可能,以及如何使用Moose语法创建一个具有此行为的Moose类。

例如为: 用户询问:$file = Repository->new(uri=>'sftp://blabla') ....并返回一个`Repository :: _ Sftp``实例

用户将使用$file,就好像它是一个Repository实例,而不需要知道真正的子类(多态)

注意:
根据要求,也许我应该更清楚我想要实现的目标:
我的类的目的是能够添加新的Repository方案(例如,通过sftp),只需创建一个“隐藏的”Repository :: _ St​​fp类,并在Repository构造函数中添加一个case来根据url生成正确的专用对象。存储库就像一个虚拟基类,提供专用对象实现的接口 所有这些都是为了添加新的存储库方案而不需要修改程序的其余部分:它会在不知不觉中处理专用实例,就好像它是一个Repository实例。

2 个答案:

答案 0 :(得分:6)

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

这里,构建器和构建的对象是不同的。建设者是一个 真实对象,带有参数,可以自定义构建 对象根据情况的要求。然后,“建立”对象是 只返回一个方法的值。这允许您构建其他 建设者视情况而定。 (建筑师的问题 功能是他们非常不灵活 - 教他们很难 一个新的特例。生成器对象仍然存在此问题, 但至少你的应用程序可以创建一个子类,实例化它, 并将此对象传递给需要创建对象的任何内容。但 在这种情况下,依赖注入是一种更好的方法。)

此外,您构建的存储库不需要继承 任何事情,他们只需要一个标签,表明他们是存储库。 这就是我们的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

现在您有了一个构建器,可以按照您喜欢的方式构建对象, 你只需要在其他代码中使用这些对象。最后一块 在谜题中是指其他的“任何”类型的Repository对象 码。这很简单,您使用does代替isa

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

你已经完成了。

答案 1 :(得分:2)

不(不是直接)。通常在Moose中,调用CLASS->new CLASS isa Moose :: Object将返回CLASS的实例。

您能否更详细地描述您想要实现的目标,以及为什么您认为这是您想要的?你可能想要构建一个 factory 类 - 当你在它上面调用一个方法时,它会调用相应的类的构造函数并将该对象返回给你,而你不必担心你的特定类型回来:

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