如何在面向对象的Perl中定义私有方法或内部方法?
我正在使用Damian Conway的“由内而外”对象,正如他精彩的著作所描述的那样,在我的客户机上构建安全系统的面向对象接口。我遇到了在我的模块中使用内部助手方法的需要,我通常会将其指定为“\u some\u method”。然而,这似乎破坏了封装,因为可以通过包名直接调用它们。有没有办法让这些方法真正私有化?例如,如何在面向对象的Perl中定义私有方法或内部方法?,perl,oop,private-methods,Perl,Oop,Private Methods,我正在使用Damian Conway的“由内而外”对象,正如他精彩的著作所描述的那样,在我的客户机上构建安全系统的面向对象接口。我遇到了在我的模块中使用内部助手方法的需要,我通常会将其指定为“\u some\u method”。然而,这似乎破坏了封装,因为可以通过包名直接调用它们。有没有办法让这些方法真正私有化?例如, use SOD::MyOOInterface; my $instance1 = SOD::MyOOInterface->new(); $instance1->_so
use SOD::MyOOInterface;
my $instance1 = SOD::MyOOInterface->new();
$instance1->_some_method; #this produces an error:
SOD::MyOOInterface::_some_method; # this results in a
# successful method call
显然,我不希望直接调用some方法成功。有没有办法保证这一点?某种程度上。您不能隐藏安装在符号表中的子例程,但可以使用词法变量保存对匿名子例程的引用:
package SOD::MyOOInterface;
my $some_method = sub { ... }
$some_method->();
由于$some_method
仅在实现类的文件中可见,因此无法从外部调用子例程。缺点是它不能作为方法调用,必须作为函数调用。如果要将其用作方法,则必须显式传递对象引用:
$some_method->($obj, @args);
我处理这个问题的方法是在方法的开头添加如下内容:
my $self = shift;
croak "Instance method called on class" unless ref $self;
这绝对不是真正的封装,但它确实意味着通过包调用您的人必须传递一个对象实例作为第一个参数。一般来说,使用Perl,我发现保护我的API不受恶意用户的攻击没有多大意义——这只是帮助我捕捉到我不小心尝试将该方法作为类方法调用的情况(这种情况比我想承认的更频繁)
就我个人而言,我认为下划线约定+将方法明确地记录为私有(或者根本不记录它,这样它就不会出现在POD中)对于实际使用来说已经足够了。这也是它在Python中的工作方式。这是不限制用户的一部分
Perl模块希望您远离它的起居室,因为您没有被邀请,而不是因为它有一把猎枪
不要将PBP用于对象实践。它很古老。事实上,现在可以在中找到有关Perl和对象的最佳实践,这几乎是Perl的必备品 简而言之,Perl模糊名称空间和类的方式大多数方法都可以在类上静态调用。这不是一件坏事,只是不要记录它。实际上没有理由想将这些方法密封到实例中。没有私有方法有点烦人,但不依赖未记录方法的惯例如此强大,足以满足我们的社区 trait实际上是一个角色(不允许实例化),可以在运行时编译成对象。这将进一步模糊典型用户的方法来源(因为它们不在原始类中),但这需要运行时成本。 有关特性的更多信息,请参阅 在前面加下划线是一个很好的惯例,用来进一步说明这个方法是窥视者的私有
最后一点,如果您真的想推动这个问题,您可以使用class::MOP::class->create_anon_class(),使用这些方法创建一个匿名类。@Micheal是的,我希望避免持有匿名子类引用的标量的“黑客”性质!如果你使用的是由内而外的对象,那么你已经在做相当“黑客”的OO了。通过代码引用隐藏私有方法似乎非常适合这种情况。@Michael实际上,一旦你习惯了每个属性作为由内而外对象的散列特性,它就变得非常自然了!您可以将其称为方法<代码>$self->$some_method()工作正常。当然,您需要使用公共方法才能使用
$self
。我同意。为什么让某人无法调用私有方法?如果没有记录,它就不是公共界面的一部分。这不仅破坏了封装,而且作者显然也不会支持它。但作者为什么要在意呢?如果我决定用我的笔记本电脑作为锤子,这显然不是我的硬件的意图,他们不会支持或修理它。但他们是不是应该千方百计阻止我这么做,还是让我自作自受。这是因为有些人认为他们应该能够通过代码建立接口,阻止人们上吊等等。下划线、未记录的方法是你必须学习的惯例,使用它甚至不会触发警告。
一些语言,如C#甚至发明了密封的
类来推动这一点。不幸的是,在一些公司,一些开发人员会忽略文档,利用他们可以利用的任何私有接口。调试Data::Dumper之类的“助手”会使插入对象的内部变得非常容易,这在某种程度上会阻碍由内向外的对象。使用由内向外的,但为了进一步说明我的观点,我们可以将inside-out对象视为基于hashref的对象。Scrotti有一个CPAN模块来实现这一点。因此,基本上,由内而外的技术不会比命名方法“\u foo”更模糊您的接口。@Randal是的,我知道了,很抱歉我不知道这个规则。我最近在“骆驼”一书中发现了这个模式,经过多年的Perl编程,我很惊讶它没有被更多地使用。但我想所有关于社区规范的回答都是这样的
package Foo;
## declare inside-out hashes here:
my %attr_a;
my %attr_b;
## declare private methods here
my $private_1 = sub {
my $self = shift;
# can use $attr_a{$self} here...
...
};
my $private_2 = sub {
my $self = shift;
...
};
## public methods here
sub new { ... }
sub public_1 {
my $self = shift;
# can access attributes here
# can call private methods too, with slightly odd syntax:
my $result = $self->$private_1(@args);
...
}
1;