Perl中的数据封装?

ste*_*ema 9 perl encapsulation

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

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

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

我有这样的东西(只是我脚本的一小部分)

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() {
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

$CurrentItem->getID()
Run Code Online (Sandbox Code Playgroud)

$CurrentItem->{ID} = "Something";
Run Code Online (Sandbox Code Playgroud)

也在工作,但我更希望,这是不可能的.

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

jus*_*tkt 12

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

本文讨论了使用Tie :: SecureHash的闭包,标量和有限访问哈希等选项,而不是最后一种方法.

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

你也可以看看你的Perl 5对象的驼鹿.它的目的是鼓励使用封装对象.


Axe*_*man 6

Perl支持部分封装:委托和信息隐藏.封装的另一部分就是我称之为"行为有限数据",将数据与行为相关联.在维基百科关于" 封装(面向对象编程) "的文章中,它暗示"封装用于指代两个相关不同的概念之一"(斜体我的).列出的第二个是

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

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

大多数动态语言通过安全封装以较重的语言完成的方式取得了很大成就.但尽管如此,Perl的允许你指定行为,以任何你想要类型的引用,并从内到外的物体可能是在Perl一样安全封装的任何其他形式的-虽然更多的是苦差事写,但只是给你一个你的情况下选择了一个安全到细节权衡那些需要它的类.


Bra*_*ert 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.
Run Code Online (Sandbox Code Playgroud)

虽然这实际上并没有阻止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};
}
Run Code Online (Sandbox Code Playgroud)

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

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

实际上,如果我已经实现了Moose为你做的一切,那么代码行的数量将是现在的两倍多.


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