如何让 Moose 返回一个子类实例而不是它自己的类,以实现多态性 [英] How to have Moose return a child class instance instead of its own class, for polymorphism

查看:16
本文介绍了如何让 Moose 返回一个子类实例而不是它自己的类,以实现多态性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to create a generic class, whose builder would not return an instance of this generic class, but an instance of a dedicated child class.

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

As Moose does automatic object building, I do not get to understand if this something possible, and how to create a Moose class with Moose syntax and having this behaviour.

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

e.g.: The user asks: $file = Repository->new(uri=>'sftp://blabla') .... and is returned an `Repository::_Sftp`` instance

用户可以像使用 Repository 实例一样使用 $file,而无需知道真正的子类(多态性)

User would use $file as if it is a Repository instance, without the need to know the real subclass (polymorphism)

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

Note:
As requested, maybe i should have been more clear about what i was trying to achieve:
The purpose of my class is to be able to add new Repository schemes (eg over sftp), by simply creating an "hidden" Repository::_Stfp class, and adding a case in the Repository constructor to factory the correct specialized object depending of url. Repository would be like a virtual base class, providing an interface that specialized objects would implement.
All of this is for adding new repository schemes without having the rest of the program to be modified: it would unknowingly deal with the specialized instance as if it is a Repository instance.

推荐答案

new 构建构建器.您想要一些其他方法来实际返回构建的对象.

new builds the builder. You want some other method to actually return the built object.

这是一个例子:

  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 {}

在这里,建造者和建造的对象是不同的.建设者是一个实物,完整的参数,可以定制构建根据情况需要的对象.然后,构建"的对象是仅返回方法的值.这允许您构建其他建设者视情况而定.(建设者的问题功能是它们非常不灵活——很难教它们一个新的特例.构建器对象仍然存在此问题,但至少你的应用程序可以创建一个子类,实例化它,并将这个对象传递给任何需要创建对象的东西.但在这种情况下,依赖注入是一种更好的方法.)

Here, the builder and the built object are distinct. The builder is a real object, complete with parameters, that can be customized to build objects as the situation demands. Then, the "built" objects are merely return values of a method. This allows you to build other builders depending on the situation. (A problem with builder functions is that they are very inflexible -- it's hard to teach them a new special case. This problem still exists with a builder object, but at least your application can create a subclass, instantiate it, and pass this object to anything that needs to create objects. But dependency injection is a better approach in this case.)

此外,您构建的存储库无需继承自任何东西,它们只需要一个标签来表明它们是存储库.这就是我们的 Repository 角色所做的.(您将要添加这里的 API 代码,以及应该重用的任何方法.不过要小心关于强制重用——你确定所有标记为存储库角色会想要那个代码吗?如果没有,只需将代码放入另一个角色并将其应用于需要该角色的类功能.)

Also, there is no need for the repositories you build to inherit from anything, they just need a tag indicating that they are repositories. And that's what our Repository role does. (You will want to add the API code here, and any methods that should be reused. But be careful about forcing reuse -- are you sure everything tagged with the Repository role will want that code? If not, just put the code in another role and apply that one to the classes that require that functionality.)

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

Here's how we use the builder we created. If, say, you don't want to touch the network:

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:

Now that you have a builder that builds the objects the way you like, you just need to use these objects in other code. So the last piece in the puzzle is referring to "any" type of Repository object in other code. That's simple, you use does instead of isa:

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

你已经完成了.

这篇关于如何让 Moose 返回一个子类实例而不是它自己的类,以实现多态性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆