在我目前的工作中,我正在构建一套严重依赖于对象的Perl脚本。 (在Hash上使用Perl的bless()
尽可能接近OO)
现在,由于缺乏更好的方法,我公司的大多数程序员都不是很聪明。更糟糕的是,他们不喜欢阅读文档,并且似乎在理解其他人的代码时遇到了问题。牛仔编码就是这里的游戏。每当他们遇到问题并尝试解决问题时,他们就会提出一个可怕的解决方案,实际上什么也解决不了,通常会使问题变得更糟。
坦率地说,这导致我不相信用鸭子类型语言编写的代码。作为一个例子,我看到太多问题,他们没有得到滥用对象的明确错误。例如,如果类型A
具有成员foo
,并且他们执行类似instance->goo
的操作,则他们不会立即看到问题。它将返回一个null / undefined值,它们可能会浪费一个小时来查找原因。然后最终改变别的东西,因为他们没有正确识别原始问题。
因此,我正在集思广益,以保持我的脚本语言(快速开发是一个优势),但在未正确使用对象时给出明确的错误消息。我意识到,由于没有编译阶段或静态类型,错误必须在运行时。我很好,只要用户得到一个非常明确的通知说“这个对象没有X”
作为我的解决方案的一部分,我不希望在尝试使用它之前检查方法/变量是否存在。
即使我的工作是在Perl中,我认为这可能与语言无关。
答案 0 :(得分:15)
如果您有任何要添加的模块,请尝试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中使子类型工作所以必须做的很少,所以更容易做到而不是! (来自Cookbook 4)
subtype 'USState'
=> as Str
=> where {
( exists $STATES->{code2state}{ uc($_) }
|| exists $STATES->{state2code}{ uc($_) } );
};
Tada,USState现在是一种你可以使用的类型!没有大惊小怪,没有麻烦,只有少量的代码。如果它不正确,它会抛出一个错误,而你的类的所有消费者必须做的是传递一个带有该字符串的标量。如果它很好(它应该是......对吗?:))他们像平常一样使用它,你的类可以防止垃圾。这太好了!
穆斯有很多很棒的东西。
相信我。看看这个。 :)
答案 1 :(得分:4)
在Perl中,
要求use strict
和use warnings
在100%的代码中处于启用状态
您可以尝试通过创建closures 来创建几乎私有的成员变量。一个很好的例子是http://www.usenix.org/publications/login/1998-10/perl.html中的“私有成员变量,排序”部分。他们不是100%私密但不明显如何访问,除非你真的知道你在做什么(并要求他们阅读你的代码并做研究以找出方法)。
如果您不想使用闭包,以下方法效果不错:
将所有对象成员变量(也称为Perl中的对象散列键)包装在访问器中。有很多方法可以从编码标准POV中有效地做到这一点。其中最不安全的是Class :: Accessor :: Fast。我确信穆斯有更好的方法,但我对穆斯并不熟悉。
确保在私人约定名称中“隐藏”实际的成员变量,例如$object->{'__private__var1'}
将是成员变量,$object->var1()
将是getter / setter访问器。
注意:对于最后一个,Class :: Accessor :: Fast是坏的,因为它的成员变量与访问者共享名称。但是你可以拥有非常简单的构建器,它们就像Class :: Accessor :: Fast一样工作,并为“foo”创建关键值,例如$ obj-> {'__ private__foo'}。
这不会阻止他们在脚下射击,但会使这样做更加困难。
在您的情况下,如果他们使用$obj->goo
或$obj->goo()
,他们将会遇到运行时错误,至少在Perl中。
他们当然可以尽力去做$obj->{'__private__goo'}
,但如果他们因为纯粹的懒惰而做了奇闻趣事牛仔废话,那么后者比做正确的$obj->foo()
要多得多。
您也可以扫描代码库,检测$object->{"_
类型的字符串,但是从您的描述可能不会起到太大的威慑作用。
答案 2 :(得分:4)
您可以使用Class::InsideOut或Object::InsideOut为您提供真正的数据隐私。不是将数据存储在受祝福的哈希引用中,而是使用受祝福的标量引用作为词汇数据哈希的关键。简而言之,如果您的同事尝试$obj->{member}
,他们将会遇到运行时错误。 $obj
中没有任何内容可供他们抓住,除了通过访问者之外,没有简单的方法来获取数据。
这是a discussion of the inside-out technique and various implementations。