Моделирование аспектов статической типизации в языке с утиной типизацией
В моей текущей работе я создаю набор сценариев 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
для них, чтобы захватить и нет простого способа получить данные, кроме как через аксессоры.