Почему бы просто не использовать __PACKAGE__ при создании нового модуля Perl? - PullRequest
6 голосов
/ 01 февраля 2020

I wi sh Я создал новую ссылку, в которой говорилось, что это лучший способ создания нового объекта в Perl:

sub new {
   my $package = shift;
   my $class = ref($package) || $package

. Так я создавал объекты для лет, но теперь мне интересно, почему go к беде? За исключением некоторых крайних случаев, почему бы просто не сделать следующее:

sub new {
    shift; # get rid of the object or package name
    my $class = __PACKAGE__;

Есть ли проблемы с простым использованием __PACKAGE__, когда нет особой причины пытаться определить, в каком пространстве имен используется метод 'new' позвонил?

Ответы [ 2 ]

11 голосов
/ 01 февраля 2020

Наследование

# classA.pm 
package ClassA;
sub new { $pkg = ref($_[0]) || $_[0] ; bless { foo => 42 }, $pkg }

# classB.pm
package ClassB;
use parent 'ClassA';
sub foo { ... }

# main.pl
use ClassA;
use ClassB;
$B = ClassB->new();
print $B->foo();

В этом примере ClassB наследует методы от ClassA, включая его конструктор. Но мы все еще хотим идентифицировать объект как принадлежащий ClassB, поэтому конструктор в ClassA должен учитывать имя ссылки, переданной его конструктору.

Проще и безопаснее, чем

$B = bless { ClassA->new(), "ClassB" };   # or
$B = bless { ClassB->new(), "ClassA" };

или добавление проходного конструктора в ClassB.

package ClassB;
use parent 'ClassA';
sub new { bless { ClassA::new(@_), __PACAKGE__ } }
6 голосов
/ 01 февраля 2020

Имя класса уже является первым аргументом конструктора, так почему бы не использовать это? Вам не нужно тянуться к чему-либо, и если вы решите, что ситуация сложнее, чем вы изначально предполагали, вы не искусственно вызвали скачок скорости. Этот код работает с наследованием или без него:

sub new { 
    my( $class, @args ) = @_;
    ...
    bless {...}, $class;
    }

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

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


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

Мне не особенно нравится такой способ создания новых объектов, и я думаю, что интерфейс, вероятно, запутает людей. Что должно произойти, когда вы вызываете new для существующего объекта? Вы клонируете существующий? Если это так, есть лучшие имена, такие как clone (Ruby использует dup). Вы получаете совершенно свободный sh объект? Если да, то почему вы go просматриваете существующий объект, чтобы получить объект, совершенно не связанный с ним?

Было время, когда во многих примерах OO Perl показывался тот же тип конструктора, и он был скопирован и наклеены во многих местах. Но с тех пор мы многому научились. Может быть, у вас есть хороший ответ, почему вы это допустили, но пока я не слышал убедительного ответа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...