Моделирование аспектов статической типизации в языке с утиной типизацией - PullRequest
8 голосов
/ 29 мая 2010

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

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

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

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

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

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

Ответы [ 3 ]

15 голосов
/ 29 мая 2010

Если у вас есть возможность добавить модули для использования, попробуйте 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($_) } );
       };

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

У Moose есть множество замечательных вещей вроде этого.

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

4 голосов
/ 30 мая 2010

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

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

4 голосов
/ 29 мая 2010

В Perl,

  • требует, чтобы use strict и use warnings были включены в 100% кода

  • Вы можете попытаться создать почти приватные переменные-члены, создав замыкания . Очень хороший пример - раздел «Private Member Variables, Sort of» в 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->{"_, хотя, по вашему описанию, это может не сработать как сдерживающий фактор.

...