10

在我目前的工作中,我正在构建一套严重依赖对象的 Perl 脚本。(在哈希上使用 Perlbless()以尽可能接近 OO)

现在,由于缺乏更好的表达方式,我公司的大多数程序员都不是很聪明。更糟糕的是,他们不喜欢阅读文档,而且似乎很难理解其他人的代码。牛仔编码是这里的游戏。每当他们遇到问题并试图解决它时,他们都会想出一个可怕的解决方案,但实际上什么也没解决,而且通常会使情况变得更糟。

坦率地说,这导致我不相信他们用鸭子类型语言编写的代码。举个例子,我看到太多的问题是他们没有因为滥用对象而没有得到明确的错误。例如,如果 typeA有 member foo,并且他们做了类似的事情instance->goo,他们不会立即看到问题。它将返回一个空值/未定义值,他们可能会浪费一个小时来寻找原因。然后最终更改其他内容,因为他们没有正确识别原始问题。

所以我正在集思广益,寻找一种方法来保留我的脚本语言(它的快速发展是一个优势),但在对象未正确使用时给出明确的错误消息。我意识到,由于没有编译阶段或静态类型,因此错误必须在运行时出现。我对此很好,只要用户得到一个非常明确的通知,说“这个对象没有 X”

作为我的解决方案的一部分,我不希望他们在尝试使用方法/变量之前检查它是否存在。

即使我的工作是在 Perl 中,我认为这可能与语言无关。

4

3 回答 3

17

如果您想添加要使用的模块,请尝试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 现在是您可以使用的类型!无需大惊小怪,只需少量代码。如果它不正确,它会抛出一个错误,你类的所有消费者所要做的就是传递一个带有该字符串的标量。如果没问题(应该是……对吗?:))他们像往常一样使用它,并且您的班级免受垃圾的侵害。这多好啊!

Moose 有很多像这样很棒的东西。

相信我。看看这个。:)

于 2010-05-29T03:09:35.630 回答
5

在 Perl 中,

  • 使其要求use strictuse warnings在 100% 的代码中启用

  • 您可以尝试通过创建闭包来创建几乎私有的成员变量。一个很好的例子是http://www.usenix.org/publications/login/1998-10/perl.html中的“私有成员变量,排序”部分。它们不是 100% 私密的,而是相当不明显的访问方式,除非您真的知道自己在做什么(并要求他们阅读您的代码并进行研究以了解如何访问)。

  • 如果您不想使用闭包,则以下方法效果不错:

    将所有对象成员变量(在 Perl 中也称为对象哈希键)包装在访问器中。有一些方法可以通过编码标准 POV 有效地做到这一点。最不安全的方法之一是 Class::Accessor::Fast。我确信 Moose 有更好的方法,但我对 Moose 并不熟悉。

    确保在私有约定名称中“隐藏”实际成员变量,例如$object->{'__private__var1'}将是成员变量,并且$object->var1()将是 getter/setter 访问器。

    注意:最后,Class::Accessor::Fast 不好,因为它的成员变量与访问器共享名称。但是您可以拥有非常简单的构建器,它们就像 Class::Accessor::Fast 一样工作,并为“foo”创建诸如 $obj->{'__private__foo'} 之类的键值。

    这不会阻止他们朝自己的脚开枪,但会让这样做变得更加困难。

    在您的情况下,如果他们使用$obj->gooor $obj->goo(),他们会得到一个运行时错误,至少在 Perl 中是这样。

    他们当然可以不遗余力地去做$obj->{'__private__goo'},但如果他们因为纯粹的懒惰而做奇闻趣事的牛仔,那么后者比做正确的工作要多得多$obj->foo()

    您还可以扫描检测$object->{"_类型字符串的代码库,尽管从您的描述来看,这可能不会起到太大的威慑作用。

于 2010-05-29T01:58:26.370 回答
4

您可以使用Class::InsideOutObject::InsideOut,它们为您提供真正的数据隐私。不是将数据存储在祝福哈希引用中,而是使用祝福标量引用作为词法数据哈希的键。长话短说,如果你的同事尝试$obj->{member}他们会得到一个运行时错误。除了通过访问器之外,他们没有任何东西$obj可以获取,也没有简单的方法可以获取数据。

这里讨论了由内而外的技术和各种实现

于 2010-05-29T21:57:26.217 回答