Что такое косвенная объектная нотация, почему она плохая и как ее избежать?

Название в значительной степени резюмирует это, но, тем не менее, вот длинная версия.

После публикации небольшого фрагмента кода Perl мне посоветовали избегать косвенной объектной нотации, «поскольку она имеет несколько побочных эффектов». Комментарий ссылался на эту конкретную строку:

my $some_object = new Some::Module(FIELD => 'value');

Так как я всегда это делал, пытаясь уложиться в срок, я спрашиваю:

  • Что в этом плохого? (конкретно)
  • Каковы возможные (предположительно отрицательные) побочные эффекты?
  • Как эту строчку переписать?

Я собирался спросить комментатора, но для меня это достойно отдельного поста.


person Jarmund    schedule 05.10.2015    source источник
comment
Мой любимый пример нотации косвенных объектов: Почему эта программа действительна? Я пытался создать синтаксическую ошибку   -  person ThisSuitIsBlackNot    schedule 05.10.2015
comment
Основная проблема с ION не столько в его использовании, сколько в его поддержке в Perl. Наличие функции вызывает множество проблем. Два, которые я могу вспомнить прямо сейчас: 1) он вызывает некоторые синтаксические ошибки, приводящие к очень странным / вводящим в заблуждение сообщениям об ошибках, 2) он препятствует реализации полезных функций, поскольку они конфликтуют с допустимым синтаксисом ION.   -  person ikegami    schedule 05.10.2015


Ответы (4)


Основная проблема в том, что это неоднозначно. Делает

my $some_object = new Some::Module(FIELD => 'value');

означает вызов метода new в пакете Some::Module или означает вызов функции new в текущем пакете с результатом вызова функции Module в пакете Some с заданными параметрами?

то есть его можно было бы проанализировать как:

# method call
my $some_object = Some::Module->new(FIELD => 'value');
# or function call
my $some_object = new(Some::Module(FIELD => 'value'));

Альтернативой является использование явной нотации вызова метода Some::Module->new(...).

Обычно синтаксический анализатор угадывает правильно, но лучше всего избегать двусмысленности.

person cjm    schedule 05.10.2015

Что в этом плохого?

Проблем с нотацией косвенного метода можно избежать, но гораздо проще сказать людям, чтобы они избегали нотации косвенного метода.

Основная проблема в том, что очень легко случайно вызвать не ту функцию. Возьмем, например, следующий код:

package Widget;

sub new { ... }
sub foo { ... }
sub bar { ... }

sub method {
   ...;
   my $o = new SubWidget;
   ...;
}

1;

Ожидается, что в этом коде new SubWidget будет означать

SubWidget->new()

Вместо этого на самом деле это означает

new("SubWidget")

Тем не менее, использование strict перехватит большинство этих случаев этой ошибки. Если бы use strict; был добавлен в приведенный выше фрагмент, возникла бы следующая ошибка:

Bareword "SubWidget" not allowed while "strict subs" in use at Widget.pm line 11.

Тем не менее, есть случаи, когда использование strict не обнаружит ошибку. В основном они включают использование скобок вокруг аргументов вызова метода (например, new SubWidget($x)).

Значит, это значит

  • Использование косвенной объектной нотации без скобок может привести к появлению странных сообщений об ошибках.
  • Использование косвенной объектной нотации с скобками может привести к вызову неправильного кода.

Первое можно терпеть, а второго можно избежать. Но вместо того, чтобы говорить людям «избегайте использования скобок вокруг аргументов вызовов методов с использованием нотации косвенного метода», мы просто говорим людям «избегайте использования нотации косвенного метода». Он слишком хрупкий.


Есть еще одна проблема. Проблема не только в использовании косвенной объектной нотации, но и в ее поддержке в Perl. Наличие функции вызывает множество проблем. В первую очередь,

  • Это вызывает некоторые синтаксические ошибки, приводящие к очень странным / вводящим в заблуждение сообщениям об ошибках, потому что код, похоже, использует ION, когда это не так.
  • Это предотвращает реализацию полезных функций, поскольку они противоречат допустимому синтаксису ION.

С другой стороны, использование no indirect; помогает решить первую проблему.


Как следует переписать эту строку?

Правильный способ написать вызов метода следующий:

my $some_object = Some::Module->new(FIELD => 'value');

Тем не менее, даже этот синтаксис неоднозначен. Сначала он проверит, существует ли функция с именем Some::Module. Но это настолько маловероятно, что очень немногие люди защищаются от подобных проблем. Если вы хотите защитить себя, вы можете использовать следующее:

my $some_object = Some::Module::->new(FIELD => 'value');
person ikegami    schedule 05.10.2015

Что касается того, как этого избежать: есть модуль CPAN, который запрещает нотацию, действуя как модуль прагмы:

no indirect;

http://metacpan.org/pod/indirect

person LeoNerd    schedule 05.10.2015
comment
Его значение ограничено, потому что я не найду проблему в коде своего ответа. - person ikegami; 06.10.2015

Комментатор просто хотел видеть Some::Module->new(FIELD => 'value'); в качестве конструктора.

Perl может использовать синтаксис косвенного объекта для других простых слов, которые выглядят так, как будто они могут быть методами, но в настоящее время perlobj документация предлагает не использовать его.

Общая проблема заключается в том, что код, написанный таким образом, неоднозначен и использует парсер Perl для проверки пространства имен, например, проверяйте, когда вы пишете method Namespace, существует ли Namespace :: method.

person Neil Slater    schedule 05.10.2015