Подтвердить что ты не робот

Как вы получаете доступ к закрытым методам или атрибутам извне типа, к которому они принадлежат?

В некоторых редких случаях, когда это действительно будет приемлемо, например, в модульных тестах, вы можете получить или установить значение закрытого атрибута или вызвать закрытый метод типа, где это не должно быть возможно. Это действительно невозможно? Если нет, как вы можете это сделать?

4b9b3361

Ответ 1

Есть два способа получить доступ к приватному методу типа и один способ получить приватные атрибуты. Все они требуют метапрограммирования, за исключением первого способа вызова частных методов, чье объяснение все равно включает метапрограммирование.

В качестве примера, мы будем реализовывать класс Hidden, который скрывает значение, используя закрытый атрибут, и класс Password, который использует Hidden для хранения пароля. Не копируйте этот пример в свой собственный код. Это не то, как вы будете разумно обрабатывать пароли; это исключительно для примера.

Вызов частных методов

Доверять другим классам

Metamodel::Trusting - это мета-роль, которая реализует поведение, необходимое для работы более высокого порядка (типы типов или виды, далее называемые HOW), чтобы иметь возможность доверять другим типам. Metamodel::ClassHOW (классы и, соответственно, грамматики) - это единственный способ, который встроен в Rakudo, который выполняет эту роль.

trusts - это ключевое слово, которое можно использовать из пакетов, чтобы позволить другому пакету вызывать свои закрытые методы (это не включает частные атрибуты). Например, грубая реализация класса контейнера паролей может выглядеть следующим образом, используя trusts:

class Password { ... }

class Hidden {
    trusts Password;

    has $!value;

    submethod BUILD(Hidden:D: :$!value) {}

    method new(Hidden:_: $value) {
        self.bless: :$value
    }

    method !dump(Hidden:D: --> Str:D) {
        $!value.perl
    }
}

class Password {
    has Hidden:_ $!value;

    submethod BUILD(Password:D: Hidden:D :$!value) {}

    method new(Password:_: Str:D $password) {
        my Hidden:D $value .= new: $password;
        self.bless: :$value
    }

    method !dump(Password:D: --> Str:D) {
        qc:to/END/;
        {self.^name}:
        $!value: {$!value!Hidden::dump}
        END
    }

    method say(Password:D: --> Nil) {
        say self!dump;
    }
}

my Password $insecure .= new: 'qwerty';
$insecure.say;

# OUTPUT:
# Password:
# $!value: "qwerty"
# 

Использование мета-метода ^ find_private_method

Metamodel::PrivateMethodContainer - это мета-роль, которая реализует поведение для HOW, которое должно содержать частные методы. Metamodel::MethodContainer и Metamodel::MultiMethodContainer - это другие мета-роли, которые реализуют поведение методов, но здесь они не будут обсуждаться. Metamodel::ClassHOW (классы и, соответственно, грамматики), Metamodel::ParametricRoleHOW и Metamodel::ConcreteRoleHOW (роли) и Metamodel::EnumHOW (перечисления) - это встроенные в Rakudo HOW, которые выполняют эту роль. Одним из методов Metamodel::PrivateMethodContainer является find_private_method, который принимает объект и имя метода в качестве параметров и либо возвращает Mu, если ни один не найден, либо экземпляр Method, представляющий метод, который вы ищете.

Пример пароля можно переписать, чтобы не использовать ключевое слово trusts, удалив строку, которая заставляет Hidden доверять Password, и изменив Password!dump на следующее:

method !dump(Password:D: --> Str:D) {
    my Method:D $dump = $!value.^find_private_method: 'dump';

    qc:to/END/;
    {self.^name}:
    $!value: {$dump($!value)}
    END
}

Получение и настройка личных атрибутов

Metamodel::AttributeContainer - это мета-роль, которая реализует поведение для типов, которые должны содержать атрибуты. В отличие от методов, это единственная мета-роль, необходимая для обработки всех типов атрибутов. Из HOW, встроенных в Rakudo, Metamodel::ClassHOW (классы и, соответственно, грамматики), Metamodel::ParametricRoleHOW и Metamodel::ConcreteRoleHOW (роли), Metamodel::EnumHOW (перечисления) и Metamodel::DefiniteHOW (используются внутри как значение [TG430) ] в методах доступа для открытых атрибутов) выполняет эту роль.

Одним из мета-методов, который Metamodel::AttributeContainer добавляет к HOW, является get_attribute_for_usage, который дает объект и имя атрибута, сбрасывает, если атрибут не найден, в противном случае возвращает экземпляр Attribute, представляющий атрибут, который вы ищете.

Attribute - это то, как атрибуты хранятся внутри Rakudo. Здесь нам нужны два метода Attribute: get_value, который принимает объект, содержащий экземпляр Attribute и возвращает его значение, и set_value, который принимает объект, содержащий экземпляр Attribute и значение и устанавливает его значение.

Пример пароля может быть переписан, поэтому Hidden не реализует закрытый метод dump, например так:

class Hidden {
    has $!value;

    submethod BUILD(Hidden:D: :$!value) {}

    method new(Hidden:_: $value) {
        self.bless: :$value;
    }
}

class Password {
    has Hidden:_ $!value;

    submethod BUILD(Password:D: Hidden:D :$!value) {}

    method new(Password:_: Str:D $password) {
        my Hidden:D $value .= new: $password;
        self.bless: :$value
    }

    method !dump(Password:D: --> Str:D) {
        my Attribute:D $value-attr = $!value.^get_attribute_for_usage: '$!value';
        my Str:D       $password   = $value-attr.get_value: $!value;

        qc:to/END/;
        {self.^name}:
        $!value: {$password.perl}
        END
    }

    method say(Password:D: --> Nil) {
        say self!dump;
    }
}

my Password:D $secure .= new: 'APrettyLongPhrase,DifficultToCrack';
$secure.say;

# OUTPUT:
# Password:
# $!value: "APrettyLongPhrase,DifficultToCrack"
#

F.A.Q.

по среднему качествуЧто делает { ... }?

Это заглушает пакет, позволяя вам объявить его до того, как вы определите его.

Что делает qc:to/END/?

Возможно, вы уже видели q:to/END/, который позволяет вам написать многострочную строку. Добавление c перед :to позволяет вставлять замыкания в строку.

Почему классы грамматики по расширению?

Грамматики используют Metamodel::GrammarHOW, который является подклассом Metamodel::ClassHOW.

Вы говорите, что ^ find_private_method и ^ get_attribute_for_usage принимают объект в качестве первого параметра, но вы опускаете его в примере. Почему?

Вызов мета-метода объекта передает себя как первый параметр неявно. Если бы мы вызывали их непосредственно на объекте КАК, мы бы передали объект в качестве первого параметра.