2010-06-08 56 views
5

我想创建一个泛型类,其构建器不会返回此泛型类的实例,而是一个专用子类的实例。由于Moose会自动构建对象,所以我不明白这是否可行,以及如何使用Moose语法创建一个Moose类并具有此行为。如何让Moose返回一个子类实例,而不是它自己的类,用于多态性

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

用户将使用$file,就好像它是一个存储库实例,而不需要知道实子类(多态性)

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

回答

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

大功告成。

+0

mmh ...让我明白这个角色的事情,回到穆斯文档... 另外,我首先想到它,因为我刚开始与穆斯,是否继续做不好的驼鹿不是驼鹿 – 2010-06-08 11:18:09

+0

@alex:如果你按照Moose :: Manual :: *中的例子,你可以使用驼鹿的大部分特征而不会遇到困难。只有当你开始使用'meta'时,事情才会变得疯狂:) – Ether 2010-06-08 16:04:33

+0

ok,理解,但我很好奇这些细微的差异: a)一个包含所有基本属性的Moose基类“Repository”专门的Moose类'Repository :: _Sftp'表示“扩展”/继承基类;一个驼鹿(或不)工厂'RepositoryBuilder',用一种方法为我创建一个专门类的实例,它可以作为'Repository'被无意识地操纵。 b)和a)一样,但是用一个角色代替基类< - 这就是你的建议,对吧? c)和d)MooseX :: AbstractFactory或MooseX :: ABC,但这些默认情况下不可用(我把centos5作为ref) – 2010-06-09 04:26:23

2

否(不直接)。一般在Moose中,调用CLASS->new其中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'); 
+0

这是我以前有过的,我也用Class :: Accessor。有人在昨天问我关于C的问题:A提到我应该尝试Moose(答案与本案无关)。所以这就是为什么我试图“ossos”我的工厂。 至于目的,我添加了一个笔记,主要问题 – 2010-06-08 11:15:26