【问题标题】:How to have Moose return a child class instance instead of its own class, for polymorphism如何让 Moose 返回一个子类实例而不是它自己的类,以实现多态性
【发布时间】:2010-06-08 00:25:27
【问题描述】:

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

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

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

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

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

【问题讨论】:

    标签: perl polymorphism moose


    【解决方案1】:

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

    你已经完成了。

    【讨论】:

    • mmh... 让我理解这个角色的事情,回到 Moose 文档... 另外,由于我刚从 Moose 开始,我首先想到的是,这样做是否更好坏驼鹿比不是驼鹿
    • @alex:如果您按照 Moose::Manual::* 中的示例进行操作,您可以毫无困难地使用 Moose 的大部分功能。只有当你开始使用meta 时,事情才会变得疯狂:)
    • 好的,明白了,但我很好奇其中的细微差别: a) Moose 基类“存储库”,具有所有基本属性; “扩展”/继承基类的专用 Moose 类“Repository::_Sftp”;一个驼鹿(或不是)工厂“RepositoryBuilder”,它有一种方法可以为我创建一个专用类的实例,可以不可知地将其作为“存储库”进行操作。 b) 与 a) 相同,但使用角色而不是基类
    【解决方案2】:

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

    【讨论】:

    • 这是我以前的一种,我也使用 Class::Accessor。昨天我问过一个关于 C:A 的问题,有人提到我应该试试 Moose(答案与本案无关)。所以这就是为什么我试图“moosify”我的那种工厂。至于目的,我在主要问题中添加了注释
    猜你喜欢
    • 2014-12-07
    • 2016-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-12
    • 1970-01-01
    • 2016-06-03
    • 1970-01-01
    相关资源
    最近更新 更多