5

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

由于 Moose 进行自动对象构建,我不明白这是否可能,以及如何使用 Moose 语法创建一个 Moose 类并具有这种行为。

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

用户会像使用$fileRepository 实例一样使用它,而无需知道真正的子类(多态性)

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

4

2 回答 2

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 代码以及应重用的任何方法。但要小心强制重用 - 您确定所有标记为 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

但如果你这样做:

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

你完成了。

于 2010-06-08T01:02:25.623 回答
2

不(不直接)。一般来说,在 Moose 中,调用CLASS->newwhere CLASS isa 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');
于 2010-06-08T00:50:36.353 回答