Моделирование аспектов статической типизации в языке с утиной типизацией

В моей текущей работе я создаю набор сценариев Perl, которые сильно зависят от объектов. (используя Perl's bless() на хеш, чтобы получить как можно ближе к ОО)

Теперь, из-за отсутствия лучшего способа выразить это, большинство программистов в моей компании не очень умны. Хуже того, они не любят читать документацию и, похоже, имеют проблемы с пониманием кода других людей. Ковбойское кодирование - это игра здесь. Всякий раз, когда они сталкиваются с проблемой и пытаются ее решить, они приходят к ужасному решению, которое на самом деле ничего не решает и обычно делает его еще хуже.

В результате я, честно говоря, не доверяю им код, написанный на языке утки. В качестве примера я вижу слишком много проблем, когда они не получают явную ошибку за неправильное использование объектов. Например, если тип A имеет члена fooи они делают что-то вроде, instance->gooони не увидят проблему немедленно. Он вернет нулевое / неопределенное значение и, вероятно, потратит час на поиск причины. Затем в конечном итоге изменить что-то еще, потому что они не смогли правильно определить исходную проблему.

Поэтому я продолжаю мозговой штурм, чтобы сохранить язык сценариев (его быстрое развитие является преимуществом), но выдает явное сообщение об ошибке, когда объект не используется должным образом. Я понимаю, что, поскольку нет стадии компиляции или статической типизации, ошибка должна быть во время выполнения. Я в порядке с этим, пока пользователь получает очень явное уведомление о том, что "этот объект не имеет X"

Как часть моего решения, я не хочу требовать, чтобы они проверяли, существует ли метод / переменная, прежде чем пытаться его использовать.

Несмотря на то, что моя работа на Perl, я думаю, что это не зависит от языка.

3 ответа

Решение

Если у вас есть возможность добавить модули для использования, попробуйте Moose. Он предоставляет практически все функции, которые вам нужны в современной среде программирования, и многое другое. Он выполняет проверку типов, отличное наследование, обладает возможностями самоанализа и с MooseX:: Declare, одним из самых приятных интерфейсов для классов Perl. Взглянуть:

use MooseX::Declare;

class BankAccount {
    has 'balance' => ( isa => 'Num', is => 'rw', default => 0 );

    method deposit (Num $amount) {
        $self->balance( $self->balance + $amount );
    }

    method withdraw (Num $amount) {
        my $current_balance = $self->balance();
        ( $current_balance >= $amount )
            || confess "Account overdrawn";
        $self->balance( $current_balance - $amount );
    }
}

class CheckingAccount extends BankAccount {
    has 'overdraft_account' => ( isa => 'BankAccount', is => 'rw' );

    before withdraw (Num $amount) {
        my $overdraft_amount = $amount - $self->balance();
        if ( $self->overdraft_account && $overdraft_amount > 0 ) {
            $self->overdraft_account->withdraw($overdraft_amount);
            $self->deposit($overdraft_amount);
        }
    }
}

Я думаю, это довольно круто, я сам.:) Это слой над объектной системой Perl, поэтому он работает с тем, что у вас уже есть (в основном).

С Moose вы можете легко создавать подтипы, чтобы убедиться, что введенные вами данные верны. Ленивые программисты соглашаются: с таким небольшим количеством того, что нужно сделать, чтобы подтипы работали в Moose, их легче сделать, чем нет! (из поваренной книги 4)

subtype 'USState'
    => as Str
    => where {
           (    exists $STATES->{code2state}{ uc($_) }
             || exists $STATES->{state2code}{ uc($_) } );
       };

И Tada, USState теперь является типом, который вы можете использовать! Нет суеты, нет суеты, и только небольшое количество кода. Он выдаст ошибку, если это не правильно, и все, что нужно потребителям вашего класса, это передать скаляр с этой строкой в ​​нем. Если это хорошо (что должно быть... правильно?:)) Они используют его как обычно, и ваш класс защищен от мусора. Как это мило!

У лося куча отличных вещей, подобных этому.

Доверьтесь мне. Проверьте это.:)

В Perl

  • сделать это требуется, чтобы use strict а также use warnings включены в 100% кода

  • Вы можете попытаться создать почти приватные переменные-члены путем создания замыканий. Очень хорошим примером является раздел "Переменные частного члена, своего рода" в http://www.usenix.org/publications/login/1998-10/perl.html. Они не на 100% приватны, но довольно неочевидны, как получить к ним доступ, если вы действительно не знаете, что делаете (и не требуют, чтобы они прочитали ваш код и провели исследование, чтобы выяснить, как)

  • Если вы не хотите использовать замыкания, следующий подход работает несколько лучше:

    Сделайте так, чтобы все переменные-члены вашего объекта (также называемые хеш-ключами объектов в Perl) были включены в методы доступа. Есть способы сделать это эффективно из стандартов кодирования POV. Одним из наименее безопасных является Class::Accessor::Fast. Я уверен, что у Муз есть лучшие способы, но я не настолько знаком с Музом.

    Убедитесь, что "скрыли" фактические переменные-члены в именах частных соглашений, например $object->{'__private__var1'} будет переменной-членом, и $object->var1() будет аксессор получения / установки

    ПРИМЕЧАНИЕ. В последнем случае Class:: Accessor:: Fast является плохим, поскольку его переменные-члены совместно используют имена с аксессорами. Но у вас могут быть очень простые компоновщики, которые работают точно так же, как Class:: Accessor:: Fast, и создают значения ключей, такие как $obj->{'__private__foo'} для "foo".

    Это не помешает им выстрелить себе в ногу, но сделает это намного сложнее.

    В вашем случае, если они используют $obj->goo или же $obj->goo(), они бы получили ошибку времени выполнения, по крайней мере, в Perl.

    Они могли бы, конечно, сделать все возможное, чтобы сделать $obj->{'__private__goo'}, но если они делают дерьмовую гонзо-ковбоя из-за явной лени, последний - намного больше работы, чем делает правильную $obj->foo(),

    Вы также можете иметь сканирование базы кода, которая обнаруживает $object->{"_ введите строки, хотя, по вашему описанию, это может не сработать как сдерживающий фактор.

Вы можете использовать Class::InsideOut или Object::InsideOut, которые обеспечивают истинную конфиденциальность данных. Вместо того, чтобы хранить данные в благословленной хеш-ссылке, благословенная скалярная ссылка используется в качестве ключа к лексическим хэшам данных. Короче говоря, если ваши коллеги попробуют $obj->{member} они получат ошибку во время выполнения. Там нет ничего в $obj для них, чтобы захватить и нет простого способа получить данные, кроме как через аксессоры.

Вот обсуждение техники наизнанку и различных реализаций.

Другие вопросы по тегам