reference - 如何在perl6中存储对父对象的引用(从perl5转换)

标签 reference parent-child raku

我正在尝试创建一个到 RPC 服务器的 Perl 6 客户端接口(interface),其类层次结构与服务器 URL 相匹配。例如

# for url path: /account/login
# client code:
$client.account.login;

为此,“子”对象(帐户)需要存储对其父对象(客户端)的引用。
这是我尝试过的:

#!/usr/bin/env perl6
use v6;

class MyApp::Client::Account {
    has $!client;

    method login() {
        # fake login
        $!client.session_id = 'abc';

        return 'ok';
    }
}

class MyApp::Client {
    has $.session_id is rw;

    method account() {
        state $empire = MyApp::Client::Account.new( :client(self) );
        return $empire;
    }
}

use Test;
plan( 2 );

my $client = MyApp::Client.new;

my $response = $client.account.login;

is( $response, 'ok', 'login successful' );

ok( $client.session_id, 'client has session_id' );

运行此命令会出现以下错误消息:

1..2
Method 'session_id' not found for invocant of class 'Any'
  in method login at test.pl6 line 9
  in block <unit> at test.pl6 line 29

# Looks like you planned 2 tests, but ran 0

我还不知道任何 perl6 类/对象习惯用法 - 我是否以正确的方式进行设计?
如果是这样,为什么 login() 方法中的 $!client 未定义?

作为引用,这是我尝试转换的 perl5(基本)版本:

#!/usr/bin/env perl
package MyApp::Client::Account;

sub new {
    my $class = shift;
    return bless {@_}, $class;
}

sub login {
    my $self = shift;
    # fake login
    $self->{client}->session_id( 'abc' );
    return 'ok';
}

package MyApp::Client;

sub new {
    my $class = shift;
    return bless {@_}, $class;
}

sub session_id {
    my $self = shift;
    if (@_) {
        $self->{session_id} = shift;
    }
    return $self->{session_id};
}

sub account {
    my $self = shift;
    $self->{account} ||= MyApp::Client::Account->new( client => $self );
    return $self->{account};
}

package main;
use Test::More tests => 2;

my $client = MyApp::Client->new;

my $response = $client->account->login;

is( $response, 'ok', 'login successful' );

ok( $client->session_id, 'client has session_id' );

这给出了预期的输出:

1..2
ok 1 - login successful
ok 2 - client has session_id

最佳答案

因此,Perl 6 OO 在一些方面与我使用过的其他实现不同。其中之一是它会自动为您填充成员变量的绝佳方式。但是,这仅在使用公共(public)访问器定义它们时才有效。

class G {
   has $!g;
   has $.e;
   method emit { say (defined $!g) ?? "$!g G thing" !! "nada G thing" }
}

这将导致以下行为:

> my $g = G.new( :g('unit-of-some-kind'), :e('electric') )
G.new(e => "electric")
> $g.emit
nada G thing
> $g.e
electric

因此,当您将 self 作为对 MyApp::Client::Account 的引用传递时,它不会绑定(bind)到 $!client 变量,因为默认构造函数只会绑定(bind)到可公开访问的成员变量。

您可以选择使其可访问,也可以将对象构造逻辑掌握在您自己的手中。如果我需要自己的 Perl 6 版本,但必须保持客户端私有(private),我想象的代码如下:

class MyApp::Client::Account {
    has $!client;

    method new(:$client) {
        self.bless( :$client );
    }

    # binds $!client to $client automatically based on the signature
    submethod BUILD(:$!client) { }

    method login() {
        # fake login
        $!client.session_id = 'abc';

        return 'ok';
    }
}

class MyApp::Client {
    has $.session_id is rw;
    has $.account;

    # the default .new will call .bless for us, which will run this BUILD
    submethod BUILD {
        $!account = MyApp::Client::Account.new( :client(self) );
    }
}

可能需要一些时间来适应newBUILD 的区别。一个关键的区别点是 selfnew 范围内不可用,但它BUILD< 范围内可用 (尽管尚未完全构建)。

关于reference - 如何在perl6中存储对父对象的引用(从perl5转换),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34944101/

相关文章:

dll - 使用 Interop.SHDocVw.dll 我在哪里可以找到这个命名空间/dll?

c# - 为什么在创建和返回新结构时出现此错误?

rest - 使用 Ext JS 访问复杂的 REST 资源

postgresql - Postgres 表继承 : move from parent to child and vice versa

mysql - 如何在 MySQL 中获取 parent 浏览次数最多的 child

raku - 使用特征应用代理

c++ - 用于引用分配的运算符重载

Javascript 函数 - 通过引用复制,但是这里发生了什么?

raku - Perl 6中触发器运算符的使用

signature - Perl 6 签名中的匿名参数是否会丢弃该值?