Perl中的数据封装?

时间:2011-03-24 13:44:54

标签: perl encapsulation

你好Perl社区的SO。我使用Perl已有几年了,但是因为我遵循了这个,所以我认识到Perl还不够。

在过去的4年里,我写了一篇非常大的剧本,试图用OO风格来做这件事。我知道Perl< 6并不是真的OO。

所以我不喜欢的一点是我没有数据封装,这意味着没有对包(“类”)真正私有的变量(或者我可能不知道如何去做)。 / p>

我有类似的东西(只是我脚本的一小部分)

package TAG;

sub new () {
    my $classname = shift;
    my $self      = {};

    bless( $self, $classname );
    $self->initialize();
    return $self;
}

sub initialize() {
    my $self = shift;

    # Only an example, I have a long list of items in this "class"
    $self->{ID}          = "NA"; 
}

sub setID() {
    ...
}

sub getID() {
    ...
}

在我的主脚本中,我正以这种方式使用它:

my $CurrentItem;
$CurrentItem = new TAG();

$CurrentItem->getID()

但是

$CurrentItem->{ID} = "Something";

也在运作,但我更愿意,这是不可能的。

有没有办法更好地封装我在“类”中使用的数据,以便我(或其他用户)被迫使用get和set方法?

5 个答案:

答案 0 :(得分:12)

这是一个已在多个地方讨论过的问题,有几种可能的解决方法,但它们都不一定是理想的。

This paper使用Tie :: SecureHash讨论闭包,标量和有限访问哈希等选项,而不是最后一种方法。

This blog认为在perl中有时候应该违反封装,尽管这些评论会带来一些负面影响。

您还可以查看moose的Perl 5对象。它的目的是鼓励使用encapsulated objects

答案 1 :(得分:6)

Perl支持封装的部分:委托和信息隐藏。封装的另一部分就是我称之为“行为有限数据”,将数据与行为相关联。在维基百科关于“Encapsulation (object-oriented programming)”的文章中,它暗示“封装用于指代两个相关的中的一个,但不同的概念”(italics mine)。列出的第二个是

  
      
  • 一种语言结构,有助于将数据与对该数据进行操作的方法(或其他功能)捆绑在一起。
  •   

文章的很大一部分是“信息隐藏”。 Perl允许隐藏复杂性的OO类型,只要你看起来不太难。我在Perl中一直使用封装 。我解决了很多问题并且一次又一次地使用,并认为只要接口类没有“到达”,行为就应该如预期的那样。

大多数动态语言通过 manners 实现了很多功能,而较重的语言通过安全封装实现了这些功能。但是,Perl允许您为任何类型的引用分配行为,并且Inside-Out objects在Perl中可能与任何其他形式的封装一样安全 - 尽管更多的是一件苦差事写,但这只是给你一个案例,你选择一个安全到详细的权衡需要它的类。

答案 2 :(得分:5)

您可以尝试Moose(或MouseAny::Moose)。

package TAG;
use Moose;

has ID => (
  reader => 'getID', # this is the only one that is needed
  writer => 'setID',
  predicate => 'hasID',
  clearer => 'clearID',
  isa => 'Str', # this makes setID smarter
  required => 1, # forces it to be included in new
);

has name => (
  is => 'ro', # same as reader => 'name',
  required => 1, # forces it to be included in new
);

# Notice that you don't have to make your own constructor.
# In fact, if you did, you would effectively break Moose
# so don't do that.

虽然这实际上并没有阻止TAG->{ID}访问,但它会为你做几件事。

大致相当于:

package TAG;
use strict;
use warnings;

sub getID{
  my($self) = @_;
  return $self->{ID};
}

sub setID{
  my($self,$new_id) = @_;

  # die if $new_id is anything other than a string
  # ( the one produced by Moose is smarter )
  die if ref $new_id;
  die unless defined $new_id;

  $self->{ID} = $new_id
}

sub hasID{
  my($self) = @_;
  return exists $self->{ID}
}

sub clearID{
  my($self) = @_;
  delete $self->{ID}
}

sub name{
  my($self) = @_;
 return $self->{name}
}

# The constructor provided by Moose is vastly superior
# to the one shown here.
sub new{
  my($class,%opt) = @_;

  my $self = bless {}, $class;

  die unless exists $opt{ID}; # this was the required => 1, from above
  $self->setID($opt{ID});

  die unless exists $opt{name}; # this was the required => 1, from above
  $self->{name} = $opt{name};
}

有一种理论认为,给定代码段中的错误的数量与代码的的数量成正比。

如果那是真的,那么我会说Moose版本的错误明显减少。

实际上,如果我已经实现了Moose为你做的所有事情,那么代码行的数量将是现在的两倍多。


如果你真的需要来阻止$TAG->{ID}访问,你可以使用另一个元类,同时仍然使用Moose 不要问我怎么做,我最近才开始使用Moose

答案 3 :(得分:2)

perl中最直接的解决方案就是将该变量加上前缀(即“_ID”,“private_ID”或任何你想要的东西),然后不记录,因为它不是接口的一部分。

还有其他方法可以做到,但是你必须要问谁会试图像这样打破它,如果他们想要滥用你的代码那么你无论如何都会遇到麻烦。

答案 4 :(得分:2)

如果你想要封装,为什么要在构造函数中破解它?而不是直接重置$self->{ID},您应该使用setID方法进行设置。

你可以做各种各样的时髦东西,以防止访问和修改你的对象内部。

最简单的方法之一是使用lock_hash锁定对象。这样,任何修改其内部的尝试都会造成致命的错误。

您也可以使用由内而外的物体。

您可以重载哈希访问以防止访问子类外部:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;


my $f = Foo->new();

$f->bar(15);

print Dumper $f;

eval {
    $f->{bar} = 23;
    1;
} or do {
    print "Direct access error: $@";
};

print Dumper $f;

BEGIN {
    package Foo;
    use strict;
    use warnings;

    use overload '%{}' => '_HASH_DEREF';

    sub new {
        return bless {};
    }

    sub bar {
        my $self = shift;
        $self->{bar} = shift if @_;
        return $self->{bar};
    }

    sub _HASH_DEREF {
        my $caller = caller;

        die "Illegal access to object internals"
            unless $caller eq __PACKAGE__;

        return shift;
    }

}

您可以祝福使用方法名称调用的子例程,以生成用作S3存储桶的魔术秘密值。

你可以想到隐藏数据的任何奇怪的事情。

Perl对象让你可以在后端做任何你想做的事情来处理存储和/或封装。

如果你有一个欺骗并直接访问对象内部的团队,那么你就有一个社会问题需要解决。技术修复 MAY 帮助。真正的解决方法是改变开发团队的行为。