代码之家  ›  专栏  ›  技术社区  ›  Alex F

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

  •  5
  • Alex F  · 技术社区  · 16 年前

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

    因为moose进行自动对象构建,所以我无法理解这是否可能,以及如何用moose语法创建moose类并具有这种行为。

    例如。: 用户要求: $file = Repository->new(uri=>'sftp://blabla') …并返回“repository::\u sftp``实例

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

    注:
    按照要求,也许我应该更清楚地了解我试图实现的目标:
    我的类的目的是通过简单地创建一个“隐藏”的存储库::stfp类,并在存储库构造函数中添加一个案例,根据URL来工厂化正确的专门化对象,从而添加新的存储库方案(如在sftp上)。存储库就像一个虚拟的基类,提供一个专门对象将实现的接口。
    所有这些都是为了在不修改程序其余部分的情况下添加新的存储库方案:它将在不知情的情况下像处理存储库实例一样处理专门的实例。

    2 回复  |  直到 15 年前
        1
  •  6
  •   jrockway    16 年前

    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
    

    现在你有了一个构建器,可以按照你喜欢的方式构建对象, 您只需要在其他代码中使用这些对象。所以最后一段 在拼图中,指的是“任何”类型的存储库对象 代码。很简单,你用 does 而不是 isa :

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

    你就完了。

        2
  •  2
  •   Ether    15 年前

    不(不直接)。一般在驼鹿,打电话 CLASS->new 其中,类isa moose::object将返回类的实例。

    你能更详细地描述一下你想要达到的目标,以及为什么你认为这是你想要的?你可能想建立一个 工厂 类——当您对它调用一个方法时,它将调用适当的类的构造函数并将该对象返回给您,而不必担心返回的特定类型:

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