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

查看:88
本文介绍了多态性如何使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

用户将使用$file就像它是一个Repository实例一样,而无需知道真正的子类(多态)

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

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

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天全站免登陆