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

Ale*_*x F 5 polymorphism perl moose

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

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

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

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

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

jro*_*way 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 {}
Run Code Online (Sandbox Code Playgroud)

这里,构建器和构建的对象是不同的.构建器是一个真实的对象,带有参数,可以根据情况需要自定义构建对象.然后,"构建"对象仅仅是方法的返回值.这允许您根据情况构建其他构建器.(构建器函数的一个问题是它们非常不灵活 - 很难教给它们一个新的特殊情况.这个问题仍然存在于构建器对象中,但至少你的应用程序可以创建一个子类,实例化它并传递这个对象对于任何需要创建对象的东西.但在这种情况下依赖注入是一种更好的方法.)

此外,您构建的存储库不需要从任何东西继承,它们只需要一个标记,指示它们是存储库.这就是我们的Repository角色所做的.(你需要在这里添加API代码,以及任何应该重用的方法.但是要注意强制重用 - 你确定用Repository角色标记的所有内容都需要那些代码吗?如果没有,只需将代码放在另一个代码中角色并将其应用于需要该功能的类.)

以下是我们如何使用我们创建的构建器.例如,如果您不想触摸网络:

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
Run Code Online (Sandbox Code Playgroud)

但如果你这样做:

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

现在你有了一个按照你喜欢的方式构建对象的构建器,你只需要在其他代码中使用这些对象.因此,拼图中的最后一部分是指其他代码中的"任何"类型的Repository对象.这很简单,你使用does而不是isa:

class SomethingThatHasARepository {
    has 'repository' => (
       is       => 'ro',
       does     => 'Repository',
       required => 1,
    );
}
Run Code Online (Sandbox Code Playgroud)

而且你已经完成了.