用鸭子型语言模拟静态类型的各个方面

Mik*_*ike 8 language-agnostic perl duck-typing static-typing

在我目前的工作中,我正在构建一套严重依赖于对象的Perl脚本.(bless()在Hash上使用Perl 来尽可能接近OO)

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

坦率地说,这导致我不相信用鸭子类型语言编写的代码.作为一个例子,我看到太多问题,他们没有得到滥用对象的明确错误.例如,如果type A有成员foo,并且他们执行类似的操作instance->goo,则他们不会立即看到问题.它将返回一个null/undefined值,它们可能会浪费一个小时来查找原因.然后最终改变别的东西,因为他们没有正确识别原始问题.

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

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

尽管我的工作是在Perl中,但我认为这可能与语言无关.

Rob*_*t P 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我觉得这很酷,我自己.:)它是Perl对象系统的一层,所以它适用于你已经拥有的东西(基本上.)

使用Moose,您可以非常轻松地创建子类型,这样您就可以确保输入有效.懒惰的程序员一致认为:由于在Moose中使子类型工作所以必须做的很少,所以更容易做到而不是!(来自Cookbook 4)

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

而Tada,USState现在是一种你可以使用的类型!没有大惊小怪,没有麻烦,只有少量的代码.如果它不正确,它会抛出一个错误,而你的类的所有消费者必须做的是传递一个带有该字符串的标量.如果它很好(它应该是......对吗?:))他们像平常一样使用它,你的类可以防止垃圾.那太好了!

穆斯有很多很棒的东西.

相信我.看看这个.:)

  • 查看MooseX :: InsideOut.它应该使$ object - > {member}无效,同时仍能使用上述所有的antlered goodness. (2认同)

DVK*_*DVK 5

在 Perl 中,

  • 使它需要use strict并且use 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->{"_类型字符串的代码库,尽管从您的描述中可能不会起到太大的威慑作用。

  • @jrockway - 足够公平 - 我的措辞不好。我将删除exaggregatrion。但正如我在“私下”选项中所说的,游戏的名称是“做坏事比做好事更难”。 (3认同)